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 #include "src/inspector/v8-debugger.h" |
| 16 #include "src/inspector/v8-inspector-impl.h" |
| 17 |
| 18 using namespace v8_inspector; |
| 19 using namespace v8; |
| 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 { |
| 43 public: |
| 44 struct TransLocation { |
| 45 WasmTranslation *translation; |
| 46 String16 script_id; |
| 47 int line; |
| 48 int column; |
| 49 int context_group_id; |
| 50 TransLocation(WasmTranslation *translation, String16 script_id, int line, |
| 51 int column, int context_group_id) |
| 52 : translation(translation), |
| 53 script_id(script_id), |
| 54 line(line), |
| 55 column(column), |
| 56 context_group_id(context_group_id) {} |
| 57 }; |
| 58 |
| 59 virtual void Translate(TransLocation *loc) = 0; |
| 60 virtual void TranslateBack(TransLocation *loc) = 0; |
| 61 |
| 62 class RawTranslator; |
| 63 class DisassemblingTranslator; |
| 64 }; |
| 65 |
| 66 class WasmTranslation::TranslatorImpl::RawTranslator |
| 67 : public WasmTranslation::TranslatorImpl { |
| 68 public: |
| 69 void Translate(TransLocation *loc) {} |
| 70 void TranslateBack(TransLocation *loc) {} |
| 71 }; |
| 72 |
| 73 class WasmTranslation::TranslatorImpl::DisassemblingTranslator |
| 74 : public WasmTranslation::TranslatorImpl { |
| 75 using OffsetTable = std::vector<std::tuple<uint32_t, int, int>>; |
| 76 |
| 77 public: |
| 78 DisassemblingTranslator(Isolate *isolate, Local<Object> script) |
| 79 : script_(isolate, script) {} |
| 80 |
| 81 void Translate(TransLocation *loc) { |
| 82 const OffsetTable &offset_table = GetOffsetTable(loc); |
| 83 DCHECK(!offset_table.empty()); |
| 84 uint32_t byte_offset = static_cast<uint32_t>(loc->column); |
| 85 |
| 86 // Binary search for the given offset. |
| 87 unsigned left = 0; // inclusive |
| 88 unsigned right = static_cast<unsigned>(offset_table.size()); // exclusive |
| 89 while (right - left > 1) { |
| 90 unsigned mid = (left + right) / 2; |
| 91 if (std::get<0>(offset_table[mid]) <= byte_offset) { |
| 92 left = mid; |
| 93 } else { |
| 94 right = mid; |
| 95 } |
| 96 } |
| 97 |
| 98 loc->script_id = GetFakeScriptId(loc); |
| 99 if (std::get<0>(offset_table[left]) == byte_offset) { |
| 100 loc->line = std::get<1>(offset_table[left]); |
| 101 loc->column = std::get<2>(offset_table[left]); |
| 102 } else { |
| 103 loc->line = 0; |
| 104 loc->column = 0; |
| 105 } |
| 106 } |
| 107 |
| 108 void TranslateBack(TransLocation *loc) { |
| 109 int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id); |
| 110 const OffsetTable *reverse_table = GetReverseTable(func_index); |
| 111 if (!reverse_table) return; |
| 112 DCHECK(!reverse_table->empty()); |
| 113 |
| 114 // Binary search for the given line and column. |
| 115 unsigned left = 0; // inclusive |
| 116 unsigned right = static_cast<unsigned>(reverse_table->size()); // exclusive |
| 117 while (right - left > 1) { |
| 118 unsigned mid = (left + right) / 2; |
| 119 auto &entry = (*reverse_table)[mid]; |
| 120 if (std::get<1>(entry) < loc->line || |
| 121 (std::get<1>(entry) == loc->line && |
| 122 std::get<2>(entry) <= loc->column)) { |
| 123 left = mid; |
| 124 } else { |
| 125 right = mid; |
| 126 } |
| 127 } |
| 128 |
| 129 int found_byte_offset = 0; |
| 130 // If we found an exact match, use it. Otherwise check whether the next |
| 131 // bigger entry is still in the same line. Report that one then. |
| 132 if (std::get<1>((*reverse_table)[left]) == loc->line && |
| 133 std::get<2>((*reverse_table)[left]) == loc->column) { |
| 134 found_byte_offset = std::get<0>((*reverse_table)[left]); |
| 135 } else if (left + 1 < reverse_table->size() && |
| 136 std::get<1>((*reverse_table)[left + 1]) == loc->line) { |
| 137 found_byte_offset = std::get<0>((*reverse_table)[left + 1]); |
| 138 } |
| 139 |
| 140 v8::Isolate *isolate = loc->translation->isolate_; |
| 141 loc->script_id = |
| 142 String16::fromInteger(GetScriptId(isolate, script_.Get(isolate))); |
| 143 loc->line = func_index; |
| 144 loc->column = found_byte_offset; |
| 145 } |
| 146 |
| 147 private: |
| 148 String16 GetFakeScriptUrl(const TransLocation *loc) { |
| 149 v8::Isolate *isolate = loc->translation->isolate_; |
| 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 } |
| 154 |
| 155 String16 GetFakeScriptId(const TransLocation *loc) { |
| 156 return String16::concat(loc->script_id, '-', |
| 157 String16::fromInteger(loc->line)); |
| 158 } |
| 159 |
| 160 int GetFunctionIndexFromFakeScriptId(const String16 &fake_script_id) { |
| 161 size_t last_dash_pos = fake_script_id.reverseFind('-'); |
| 162 DCHECK_GT(fake_script_id.length(), last_dash_pos); |
| 163 bool ok = true; |
| 164 int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok); |
| 165 DCHECK(ok); |
| 166 return func_index; |
| 167 } |
| 168 |
| 169 const OffsetTable &GetOffsetTable(const TransLocation *loc) { |
| 170 int func_index = loc->line; |
| 171 auto it = offset_tables_.find(func_index); |
| 172 if (it != offset_tables_.end()) return it->second; |
| 173 |
| 174 v8::Isolate *isolate = loc->translation->isolate_; |
| 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; |
| 193 } |
| 194 |
| 195 const OffsetTable *GetReverseTable(int func_index) { |
| 196 auto it = reverse_tables_.find(func_index); |
| 197 if (it != reverse_tables_.end()) return &it->second; |
| 198 |
| 199 // Find offset table, copy and sort it to get reverse table. |
| 200 it = offset_tables_.find(func_index); |
| 201 if (it == offset_tables_.end()) return nullptr; |
| 202 |
| 203 OffsetTable reverse_table = it->second; |
| 204 // Order by line, column, then byte offset. |
| 205 auto cmp = [](std::tuple<uint32_t, int, int> el1, |
| 206 std::tuple<uint32_t, int, int> el2) { |
| 207 if (std::get<1>(el1) != std::get<1>(el2)) |
| 208 return std::get<1>(el1) < std::get<1>(el2); |
| 209 if (std::get<2>(el1) != std::get<2>(el2)) |
| 210 return std::get<2>(el1) < std::get<2>(el2); |
| 211 return std::get<0>(el1) < std::get<0>(el2); |
| 212 }; |
| 213 std::sort(reverse_table.begin(), reverse_table.end(), cmp); |
| 214 |
| 215 auto inserted = reverse_tables_.insert( |
| 216 std::make_pair(func_index, std::move(reverse_table))); |
| 217 DCHECK(inserted.second); |
| 218 return &inserted.first->second; |
| 219 } |
| 220 |
| 221 Global<Object> script_; |
| 222 |
| 223 // We assume to only disassemble a subset of the functions, so store them in a |
| 224 // map instead of an array. |
| 225 std::unordered_map<int, const OffsetTable> offset_tables_; |
| 226 std::unordered_map<int, const OffsetTable> reverse_tables_; |
| 227 }; |
| 228 |
| 229 WasmTranslation::WasmTranslation(v8::Isolate *isolate, V8Debugger *debugger) |
| 230 : isolate_(isolate), debugger_(debugger), mode_(Disassemble) {} |
| 231 |
| 232 WasmTranslation::~WasmTranslation() { Clear(); } |
| 233 |
| 234 void WasmTranslation::AddScript(Local<Object> script_wrapper) { |
| 235 int script_id = GetScriptId(isolate_, script_wrapper); |
| 236 DCHECK_EQ(0U, wasm_translators_.count(script_id)); |
| 237 std::unique_ptr<TranslatorImpl> impl; |
| 238 switch (mode_) { |
| 239 case Raw: |
| 240 impl.reset(new TranslatorImpl::RawTranslator()); |
| 241 break; |
| 242 case Disassemble: |
| 243 impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, |
| 244 script_wrapper)); |
| 245 break; |
| 246 } |
| 247 DCHECK(impl); |
| 248 wasm_translators_.insert(std::make_pair(script_id, std::move(impl))); |
| 249 } |
| 250 |
| 251 void WasmTranslation::Clear() { |
| 252 wasm_translators_.clear(); |
| 253 fake_scripts_.clear(); |
| 254 } |
| 255 |
| 256 // Translation "forward" (to artificial scripts). |
| 257 bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation( |
| 258 String16 *script_id, int *line_number, int *column_number, |
| 259 int context_group_id) { |
| 260 DCHECK(script_id && line_number && column_number); |
| 261 bool ok = true; |
| 262 int script_id_int = script_id->toInteger(&ok); |
| 263 if (!ok) return false; |
| 264 |
| 265 auto it = wasm_translators_.find(script_id_int); |
| 266 if (it == wasm_translators_.end()) return false; |
| 267 TranslatorImpl *translator = it->second.get(); |
| 268 |
| 269 TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id), |
| 270 *line_number, *column_number, |
| 271 context_group_id); |
| 272 translator->Translate(&trans_loc); |
| 273 |
| 274 *script_id = std::move(trans_loc.script_id); |
| 275 *line_number = trans_loc.line; |
| 276 *column_number = trans_loc.column; |
| 277 |
| 278 return true; |
| 279 } |
| 280 |
| 281 // Translation "backward" (from artificial to real scripts). |
| 282 bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation( |
| 283 String16 *script_id, int *line_number, int *column_number) { |
| 284 auto it = fake_scripts_.find(*script_id); |
| 285 if (it == fake_scripts_.end()) return false; |
| 286 TranslatorImpl *translator = it->second; |
| 287 |
| 288 TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id), |
| 289 *line_number, *column_number, -1); |
| 290 translator->TranslateBack(&trans_loc); |
| 291 |
| 292 *script_id = std::move(trans_loc.script_id); |
| 293 *line_number = trans_loc.line; |
| 294 *column_number = trans_loc.column; |
| 295 |
| 296 return true; |
| 297 } |
| 298 |
| 299 void WasmTranslation::AddFakeScript( |
| 300 std::unique_ptr<V8DebuggerScript> fake_script, TranslatorImpl *translator, |
| 301 int context_group_id) { |
| 302 bool inserted = |
| 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 } |
OLD | NEW |