| Index: src/inspector/wasm-translation.cc
 | 
| diff --git a/src/inspector/wasm-translation.cc b/src/inspector/wasm-translation.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..0775460ae715f9b3bcfabcf539a0f7e77fa3938e
 | 
| --- /dev/null
 | 
| +++ b/src/inspector/wasm-translation.cc
 | 
| @@ -0,0 +1,310 @@
 | 
| +// Copyright 2016 the V8 project authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "src/inspector/wasm-translation.h"
 | 
| +
 | 
| +#include <algorithm>
 | 
| +
 | 
| +#include "src/debug/debug-interface.h"
 | 
| +#include "src/inspector/protocol/Debugger.h"
 | 
| +#include "src/inspector/script-breakpoint.h"
 | 
| +#include "src/inspector/string-util.h"
 | 
| +#include "src/inspector/v8-debugger-agent-impl.h"
 | 
| +#include "src/inspector/v8-debugger-script.h"
 | 
| +#include "src/inspector/v8-debugger.h"
 | 
| +#include "src/inspector/v8-inspector-impl.h"
 | 
| +
 | 
| +using namespace v8_inspector;
 | 
| +using namespace v8;
 | 
| +
 | 
| +namespace {
 | 
| +int GetScriptId(Isolate *isolate, Local<Object> script_wrapper) {
 | 
| +  Local<Value> script_id = script_wrapper
 | 
| +                               ->Get(isolate->GetCurrentContext(),
 | 
| +                                     toV8StringInternalized(isolate, "id"))
 | 
| +                               .ToLocalChecked();
 | 
| +  DCHECK(script_id->IsInt32());
 | 
| +  return script_id->Int32Value(isolate->GetCurrentContext()).FromJust();
 | 
| +}
 | 
| +
 | 
| +String16 GetScriptName(Isolate *isolate, Local<Object> script_wrapper) {
 | 
| +  Local<Value> script_name = script_wrapper
 | 
| +                                 ->Get(isolate->GetCurrentContext(),
 | 
| +                                       toV8StringInternalized(isolate, "name"))
 | 
| +                                 .ToLocalChecked();
 | 
| +  DCHECK(script_name->IsString());
 | 
| +  return toProtocolString(script_name.As<String>());
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +class WasmTranslation::TranslatorImpl {
 | 
| + public:
 | 
| +  struct TransLocation {
 | 
| +    WasmTranslation *translation;
 | 
| +    String16 script_id;
 | 
| +    int line;
 | 
| +    int column;
 | 
| +    int context_group_id;
 | 
| +    TransLocation(WasmTranslation *translation, String16 script_id, int line,
 | 
| +                  int column, int context_group_id)
 | 
| +        : translation(translation),
 | 
| +          script_id(script_id),
 | 
| +          line(line),
 | 
| +          column(column),
 | 
| +          context_group_id(context_group_id) {}
 | 
| +  };
 | 
| +
 | 
| +  virtual void Translate(TransLocation *loc) = 0;
 | 
| +  virtual void TranslateBack(TransLocation *loc) = 0;
 | 
| +
 | 
| +  class RawTranslator;
 | 
| +  class DisassemblingTranslator;
 | 
| +};
 | 
| +
 | 
| +class WasmTranslation::TranslatorImpl::RawTranslator
 | 
| +    : public WasmTranslation::TranslatorImpl {
 | 
| + public:
 | 
| +  void Translate(TransLocation *loc) {}
 | 
| +  void TranslateBack(TransLocation *loc) {}
 | 
| +};
 | 
| +
 | 
| +class WasmTranslation::TranslatorImpl::DisassemblingTranslator
 | 
| +    : public WasmTranslation::TranslatorImpl {
 | 
| +  using OffsetTable = std::vector<std::tuple<uint32_t, int, int>>;
 | 
| +
 | 
| + public:
 | 
| +  DisassemblingTranslator(Isolate *isolate, Local<Object> script)
 | 
| +      : script_(isolate, script) {}
 | 
| +
 | 
| +  void Translate(TransLocation *loc) {
 | 
| +    const OffsetTable &offset_table = GetOffsetTable(loc);
 | 
| +    DCHECK(!offset_table.empty());
 | 
| +    uint32_t byte_offset = static_cast<uint32_t>(loc->column);
 | 
| +
 | 
| +    // Binary search for the given offset.
 | 
| +    unsigned left = 0;                                            // inclusive
 | 
| +    unsigned right = static_cast<unsigned>(offset_table.size());  // exclusive
 | 
| +    while (right - left > 1) {
 | 
| +      unsigned mid = (left + right) / 2;
 | 
| +      if (std::get<0>(offset_table[mid]) <= byte_offset) {
 | 
| +        left = mid;
 | 
| +      } else {
 | 
| +        right = mid;
 | 
| +      }
 | 
| +    }
 | 
| +
 | 
| +    loc->script_id = GetFakeScriptId(loc);
 | 
| +    if (std::get<0>(offset_table[left]) == byte_offset) {
 | 
| +      loc->line = std::get<1>(offset_table[left]);
 | 
| +      loc->column = std::get<2>(offset_table[left]);
 | 
| +    } else {
 | 
| +      loc->line = 0;
 | 
| +      loc->column = 0;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  void TranslateBack(TransLocation *loc) {
 | 
| +    int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id);
 | 
| +    const OffsetTable *reverse_table = GetReverseTable(func_index);
 | 
| +    if (!reverse_table) return;
 | 
| +    DCHECK(!reverse_table->empty());
 | 
| +
 | 
| +    // Binary search for the given line and column.
 | 
| +    unsigned left = 0;                                              // inclusive
 | 
| +    unsigned right = static_cast<unsigned>(reverse_table->size());  // exclusive
 | 
| +    while (right - left > 1) {
 | 
| +      unsigned mid = (left + right) / 2;
 | 
| +      auto &entry = (*reverse_table)[mid];
 | 
| +      if (std::get<1>(entry) < loc->line ||
 | 
| +          (std::get<1>(entry) == loc->line &&
 | 
| +           std::get<2>(entry) <= loc->column)) {
 | 
| +        left = mid;
 | 
| +      } else {
 | 
| +        right = mid;
 | 
| +      }
 | 
| +    }
 | 
| +
 | 
| +    int found_byte_offset = 0;
 | 
| +    // If we found an exact match, use it. Otherwise check whether the next
 | 
| +    // bigger entry is still in the same line. Report that one then.
 | 
| +    if (std::get<1>((*reverse_table)[left]) == loc->line &&
 | 
| +        std::get<2>((*reverse_table)[left]) == loc->column) {
 | 
| +      found_byte_offset = std::get<0>((*reverse_table)[left]);
 | 
| +    } else if (left + 1 < reverse_table->size() &&
 | 
| +               std::get<1>((*reverse_table)[left + 1]) == loc->line) {
 | 
| +      found_byte_offset = std::get<0>((*reverse_table)[left + 1]);
 | 
| +    }
 | 
| +
 | 
| +    v8::Isolate *isolate = loc->translation->isolate_;
 | 
| +    loc->script_id =
 | 
| +        String16::fromInteger(GetScriptId(isolate, script_.Get(isolate)));
 | 
| +    loc->line = func_index;
 | 
| +    loc->column = found_byte_offset;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  String16 GetFakeScriptUrl(const TransLocation *loc) {
 | 
| +    v8::Isolate *isolate = loc->translation->isolate_;
 | 
| +    String16 script_name = GetScriptName(isolate, script_.Get(isolate));
 | 
| +    return String16::concat("wasm://wasm/", script_name, '/', script_name, '-',
 | 
| +                            String16::fromInteger(loc->line));
 | 
| +  }
 | 
| +
 | 
| +  String16 GetFakeScriptId(const TransLocation *loc) {
 | 
| +    return String16::concat(loc->script_id, '-',
 | 
| +                            String16::fromInteger(loc->line));
 | 
| +  }
 | 
| +
 | 
| +  int GetFunctionIndexFromFakeScriptId(const String16 &fake_script_id) {
 | 
| +    size_t last_dash_pos = fake_script_id.reverseFind('-');
 | 
| +    DCHECK_GT(fake_script_id.length(), last_dash_pos);
 | 
| +    bool ok = true;
 | 
| +    int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok);
 | 
| +    DCHECK(ok);
 | 
| +    return func_index;
 | 
| +  }
 | 
| +
 | 
| +  const OffsetTable &GetOffsetTable(const TransLocation *loc) {
 | 
| +    int func_index = loc->line;
 | 
| +    auto it = offset_tables_.find(func_index);
 | 
| +    if (it != offset_tables_.end()) return it->second;
 | 
| +
 | 
| +    v8::Isolate *isolate = loc->translation->isolate_;
 | 
| +    std::pair<std::string, OffsetTable> disassembly =
 | 
| +        DebugInterface::DisassembleWasmFunction(isolate, script_.Get(isolate),
 | 
| +                                                func_index);
 | 
| +
 | 
| +    it = offset_tables_
 | 
| +             .insert(std::make_pair(func_index, std::move(disassembly.second)))
 | 
| +             .first;
 | 
| +
 | 
| +    String16 fake_script_id = GetFakeScriptId(loc);
 | 
| +    String16 fake_script_url = GetFakeScriptUrl(loc);
 | 
| +    String16 source(disassembly.first.data(), disassembly.first.length());
 | 
| +    std::unique_ptr<V8DebuggerScript> fake_script(new V8DebuggerScript(
 | 
| +        fake_script_id, std::move(fake_script_url), source));
 | 
| +
 | 
| +    loc->translation->AddFakeScript(std::move(fake_script), this,
 | 
| +                                    loc->context_group_id);
 | 
| +
 | 
| +    return it->second;
 | 
| +  }
 | 
| +
 | 
| +  const OffsetTable *GetReverseTable(int func_index) {
 | 
| +    auto it = reverse_tables_.find(func_index);
 | 
| +    if (it != reverse_tables_.end()) return &it->second;
 | 
| +
 | 
| +    // Find offset table, copy and sort it to get reverse table.
 | 
| +    it = offset_tables_.find(func_index);
 | 
| +    if (it == offset_tables_.end()) return nullptr;
 | 
| +
 | 
| +    OffsetTable reverse_table = it->second;
 | 
| +    // Order by line, column, then byte offset.
 | 
| +    auto cmp = [](std::tuple<uint32_t, int, int> el1,
 | 
| +                  std::tuple<uint32_t, int, int> el2) {
 | 
| +      if (std::get<1>(el1) != std::get<1>(el2))
 | 
| +        return std::get<1>(el1) < std::get<1>(el2);
 | 
| +      if (std::get<2>(el1) != std::get<2>(el2))
 | 
| +        return std::get<2>(el1) < std::get<2>(el2);
 | 
| +      return std::get<0>(el1) < std::get<0>(el2);
 | 
| +    };
 | 
| +    std::sort(reverse_table.begin(), reverse_table.end(), cmp);
 | 
| +
 | 
| +    auto inserted = reverse_tables_.insert(
 | 
| +        std::make_pair(func_index, std::move(reverse_table)));
 | 
| +    DCHECK(inserted.second);
 | 
| +    return &inserted.first->second;
 | 
| +  }
 | 
| +
 | 
| +  Global<Object> script_;
 | 
| +
 | 
| +  // We assume to only disassemble a subset of the functions, so store them in a
 | 
| +  // map instead of an array.
 | 
| +  std::unordered_map<int, const OffsetTable> offset_tables_;
 | 
| +  std::unordered_map<int, const OffsetTable> reverse_tables_;
 | 
| +};
 | 
| +
 | 
| +WasmTranslation::WasmTranslation(v8::Isolate *isolate, V8Debugger *debugger)
 | 
| +    : isolate_(isolate), debugger_(debugger), mode_(Disassemble) {}
 | 
| +
 | 
| +WasmTranslation::~WasmTranslation() { Clear(); }
 | 
| +
 | 
| +void WasmTranslation::AddScript(Local<Object> script_wrapper) {
 | 
| +  int script_id = GetScriptId(isolate_, script_wrapper);
 | 
| +  DCHECK_EQ(0U, wasm_translators_.count(script_id));
 | 
| +  std::unique_ptr<TranslatorImpl> impl;
 | 
| +  switch (mode_) {
 | 
| +    case Raw:
 | 
| +      impl.reset(new TranslatorImpl::RawTranslator());
 | 
| +      break;
 | 
| +    case Disassemble:
 | 
| +      impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_,
 | 
| +                                                             script_wrapper));
 | 
| +      break;
 | 
| +  }
 | 
| +  DCHECK(impl);
 | 
| +  wasm_translators_.insert(std::make_pair(script_id, std::move(impl)));
 | 
| +}
 | 
| +
 | 
| +void WasmTranslation::Clear() {
 | 
| +  wasm_translators_.clear();
 | 
| +  fake_scripts_.clear();
 | 
| +}
 | 
| +
 | 
| +// Translation "forward" (to artificial scripts).
 | 
| +bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
 | 
| +    String16 *script_id, int *line_number, int *column_number,
 | 
| +    int context_group_id) {
 | 
| +  DCHECK(script_id && line_number && column_number);
 | 
| +  bool ok = true;
 | 
| +  int script_id_int = script_id->toInteger(&ok);
 | 
| +  if (!ok) return false;
 | 
| +
 | 
| +  auto it = wasm_translators_.find(script_id_int);
 | 
| +  if (it == wasm_translators_.end()) return false;
 | 
| +  TranslatorImpl *translator = it->second.get();
 | 
| +
 | 
| +  TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
 | 
| +                                          *line_number, *column_number,
 | 
| +                                          context_group_id);
 | 
| +  translator->Translate(&trans_loc);
 | 
| +
 | 
| +  *script_id = std::move(trans_loc.script_id);
 | 
| +  *line_number = trans_loc.line;
 | 
| +  *column_number = trans_loc.column;
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +// Translation "backward" (from artificial to real scripts).
 | 
| +bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
 | 
| +    String16 *script_id, int *line_number, int *column_number) {
 | 
| +  auto it = fake_scripts_.find(*script_id);
 | 
| +  if (it == fake_scripts_.end()) return false;
 | 
| +  TranslatorImpl *translator = it->second;
 | 
| +
 | 
| +  TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
 | 
| +                                          *line_number, *column_number, -1);
 | 
| +  translator->TranslateBack(&trans_loc);
 | 
| +
 | 
| +  *script_id = std::move(trans_loc.script_id);
 | 
| +  *line_number = trans_loc.line;
 | 
| +  *column_number = trans_loc.column;
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void WasmTranslation::AddFakeScript(
 | 
| +    std::unique_ptr<V8DebuggerScript> fake_script, TranslatorImpl *translator,
 | 
| +    int context_group_id) {
 | 
| +  bool inserted =
 | 
| +      fake_scripts_.insert(std::make_pair(fake_script->scriptId(), translator))
 | 
| +          .second;
 | 
| +  DCHECK(inserted);
 | 
| +  USE(inserted);
 | 
| +  V8DebuggerAgentImpl *agent =
 | 
| +      debugger_->inspector()->enabledDebuggerAgentForGroup(context_group_id);
 | 
| +  agent->didParseSource(std::move(fake_script), true);
 | 
| +}
 | 
| 
 |