| Index: src/wasm/wasm-debug.cc | 
| diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..65e313ba26dc1ac23739ceb72dd3dbb4841dff0f | 
| --- /dev/null | 
| +++ b/src/wasm/wasm-debug.cc | 
| @@ -0,0 +1,234 @@ | 
| +// 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/wasm/wasm-debug.h" | 
| + | 
| +#include "src/assert-scope.h" | 
| +#include "src/debug/debug.h" | 
| +#include "src/factory.h" | 
| +#include "src/isolate.h" | 
| +#include "src/wasm/module-decoder.h" | 
| +#include "src/wasm/wasm-module.h" | 
| + | 
| +using namespace v8::internal; | 
| +using namespace v8::internal::wasm; | 
| + | 
| +namespace { | 
| + | 
| +enum { | 
| +  kWasmDebugInfoWasmObj, | 
| +  kWasmDebugInfoWasmBytesHash, | 
| +  kWasmDebugInfoFunctionOffsets, | 
| +  kWasmDebugInfoNumInternalFields | 
| +}; | 
| + | 
| +ByteArray *GetOrCreateFunctionOffsetTable(WasmDebugInfo *debug_info) { | 
| +  Object *offset_table = | 
| +      debug_info->GetInternalField(kWasmDebugInfoFunctionOffsets); | 
| +  if (!offset_table->IsUndefined()) return ByteArray::cast(offset_table); | 
| + | 
| +  FunctionOffsetsResult function_offsets; | 
| +  { | 
| +    DisallowHeapAllocation no_gc; | 
| +    SeqOneByteString *wasm_bytes = | 
| +        wasm::GetWasmBytes(debug_info->wasm_object()); | 
| +    const byte *bytes_start = wasm_bytes->GetChars(); | 
| +    const byte *bytes_end = bytes_start + wasm_bytes->length(); | 
| +    function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end); | 
| +  } | 
| +  DCHECK(function_offsets.ok()); | 
| +  Isolate *isolate = debug_info->GetIsolate(); | 
| +  size_t array_size = | 
| +      2 * sizeof(int) * static_cast<int>(function_offsets.val.size()); | 
| +  CHECK_LE(array_size, std::numeric_limits<int>::max()); | 
| +  ByteArray *arr = | 
| +      *isolate->factory()->NewByteArray(static_cast<int>(array_size)); | 
| +  int idx = 0; | 
| +  for (std::pair<int, int> p : function_offsets.val) { | 
| +    arr->set_int(idx++, p.first); | 
| +    arr->set_int(idx++, p.second); | 
| +  } | 
| +  DCHECK_EQ(arr->length(), idx * sizeof(int)); | 
| +  debug_info->SetInternalField(kWasmDebugInfoFunctionOffsets, arr); | 
| + | 
| +  return arr; | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) { | 
| +  Isolate *isolate = wasm->GetIsolate(); | 
| +  Factory *factory = isolate->factory(); | 
| +  Handle<Map> map = factory->NewMap( | 
| +      JS_OBJECT_TYPE, | 
| +      JSObject::kHeaderSize + kWasmDebugInfoNumInternalFields * kPointerSize); | 
| +  Handle<JSObject> info = factory->NewJSObjectFromMap(map, TENURED); | 
| +  info->SetInternalField(kWasmDebugInfoWasmObj, *wasm); | 
| +  int hash = 0; | 
| +  Handle<SeqOneByteString> wasm_bytes(GetWasmBytes(*wasm), isolate); | 
| +  { | 
| +    DisallowHeapAllocation no_gc; | 
| +    hash = StringHasher::HashSequentialString(wasm_bytes->GetChars(), | 
| +                                              wasm_bytes->length(), 0); | 
| +  } | 
| +  info->SetInternalField(kWasmDebugInfoWasmBytesHash, | 
| +                         *factory->NewNumberFromInt(hash, TENURED)); | 
| + | 
| +  // TODO(clemensh): Register the wasm module at the debugger | 
| + | 
| +  return Handle<WasmDebugInfo>::cast(info); | 
| +} | 
| + | 
| +bool WasmDebugInfo::IsDebugInfo(Object *object) { | 
| +  if (!object->IsJSObject()) return false; | 
| +  JSObject *obj = JSObject::cast(object); | 
| +  return obj->GetInternalFieldCount() == kWasmDebugInfoNumInternalFields && | 
| +         IsWasmObject(obj->GetInternalField(kWasmDebugInfoWasmObj)) && | 
| +         obj->GetInternalField(kWasmDebugInfoWasmBytesHash)->IsNumber() && | 
| +         (obj->GetInternalField(kWasmDebugInfoFunctionOffsets)->IsUndefined() || | 
| +          obj->GetInternalField(kWasmDebugInfoFunctionOffsets)->IsByteArray()); | 
| +} | 
| + | 
| +WasmDebugInfo *WasmDebugInfo::cast(Object *object) { | 
| +  DCHECK(IsDebugInfo(object)); | 
| +  return static_cast<WasmDebugInfo *>(object); | 
| +} | 
| + | 
| +JSObject *WasmDebugInfo::wasm_object() { | 
| +  return JSObject::cast(GetInternalField(kWasmDebugInfoWasmObj)); | 
| +} | 
| + | 
| +bool WasmDebugInfo::SetBreakPoint(int byte_offset) { return false; } | 
| + | 
| +std::pair<int, int> WasmDebugInfo::GetFunctionOffsetAndLength(int func_index) { | 
| +  ByteArray *arr = GetOrCreateFunctionOffsetTable(this); | 
| + | 
| +  if (func_index < 0 || func_index >= arr->length() / sizeof(int) / 2) { | 
| +    return {0, 0}; | 
| +  } | 
| +  int offset = arr->get_int(2 * func_index); | 
| +  int length = arr->get_int(2 * func_index + 1); | 
| +  // Assert that it's distinguishable from the "illegal function index" return. | 
| +  DCHECK(offset > 0 && length > 0); | 
| +  return {offset, length}; | 
| +} | 
| + | 
| +Handle<Object> wasm::GetFunctionOffsetTable(Isolate *isolate, | 
| +                                            Handle<String> module_bytes) { | 
| +  FunctionOffsetsResult function_offsets; | 
| +  { | 
| +    DisallowHeapAllocation no_gc; | 
| +    DCHECK(module_bytes->IsSeqOneByteString()); | 
| +    Vector<const uint8_t> bytes_vec = | 
| +        module_bytes->GetFlatContent().ToOneByteVector(); | 
| +    function_offsets = | 
| +        wasm::DecodeWasmFunctionOffsets(bytes_vec.start(), bytes_vec.end()); | 
| +  } | 
| +  if (!function_offsets.ok()) return isolate->factory()->undefined_value(); | 
| + | 
| +  size_t array_size = 2 * function_offsets.val.size(); | 
| +  CHECK_LE(array_size, std::numeric_limits<int>::max()); | 
| +  Handle<FixedArray> arr = | 
| +      isolate->factory()->NewFixedArray(static_cast<int>(array_size)); | 
| +  int idx = 0; | 
| +  for (std::pair<int, int> p : function_offsets.val) { | 
| +    arr->set(idx++, Smi::FromInt(p.first)); | 
| +    arr->set(idx++, Smi::FromInt(p.second)); | 
| +  } | 
| +  DCHECK_EQ(arr->length(), idx); | 
| +  return isolate->factory()->NewJSArrayWithElements(arr); | 
| +} | 
| + | 
| +Handle<Object> wasm::DisassembleFunction(Isolate *isolate, | 
| +                                         Handle<String> function_bytes, | 
| +                                         bool get_code, bool get_table) { | 
| +  struct NullBuf : public std::streambuf {}; | 
| +  struct LineCountingStream : public std::streambuf { | 
| +    std::streambuf *buf; | 
| +    uint32_t line_nr = 0; | 
| +    uint32_t col_nr = 0; | 
| + | 
| +    explicit LineCountingStream(std::streambuf *buf) : buf(buf) {} | 
| +    inline void nextChar(int_type c) { | 
| +      ++col_nr; | 
| +      if (c == '\n') ++line_nr, col_nr = 0; | 
| +    } | 
| +    int_type overflow(int_type c) override { | 
| +      if (c != EOF) { | 
| +        nextChar(c); | 
| +        buf->sputc(c); | 
| +      } | 
| +      return c; | 
| +    } | 
| +    std::streamsize xsputn(const char *s, std::streamsize n) override { | 
| +      for (const char *p = s, *e = s + n; p != e; ++p) nextChar(*p); | 
| +      buf->sputn(s, n); | 
| +      return n; | 
| +    } | 
| +  }; | 
| + | 
| +  // The result will be put here. | 
| +  std::stringbuf code_buf; | 
| +  std::vector<uint32_t> offset_table; | 
| + | 
| +  // If get_code is set, use ostringstream. If get_table is set, use | 
| +  // LineCountingStream. Both can be combined. | 
| +  NullBuf nullbuf; | 
| +  std::streambuf *selected_streambuf = | 
| +      get_code ? &code_buf : static_cast<std::streambuf *>(&nullbuf); | 
| +  LineCountingStream line_counting_stream(selected_streambuf); | 
| +  std::ostream line_counting_ostream(&line_counting_stream); | 
| +  std::function<void(int)> instruction_callback; | 
| +  if (get_table) { | 
| +    instruction_callback = [&](uint32_t offset) { | 
| +      offset_table.push_back(offset); | 
| +      offset_table.push_back(line_counting_stream.line_nr); | 
| +      offset_table.push_back(line_counting_stream.col_nr); | 
| +    }; | 
| +    selected_streambuf = &line_counting_stream; | 
| +  } | 
| + | 
| +  { | 
| +    DisallowHeapAllocation no_gc; | 
| +    DCHECK(function_bytes->IsSeqOneByteString()); | 
| +    Vector<const uint8_t> bytes_vec = | 
| +        function_bytes->GetFlatContent().ToOneByteVector(); | 
| + | 
| +    base::AccountingAllocator allocator; | 
| +    std::ostream os(selected_streambuf); | 
| +    if (!PrintAst(&allocator, | 
| +                  FunctionBody::ForTesting(bytes_vec.start(), bytes_vec.end()), | 
| +                  os, instruction_callback)) { | 
| +      return isolate->factory()->undefined_value(); | 
| +    } | 
| +  } | 
| + | 
| +  Handle<FixedArray> return_arr = isolate->factory()->NewFixedArray(2); | 
| + | 
| +  if (get_code) { | 
| +    // Unfortunately, we have to copy the string here. | 
| +    std::string code_str = code_buf.str(); | 
| +    CHECK_LE(code_str.length(), std::numeric_limits<int>::max()); | 
| +    return_arr->set( | 
| +        0, *isolate->factory() | 
| +                ->NewStringFromAscii(Vector<const char>( | 
| +                    code_str.data(), static_cast<int>(code_str.length()))) | 
| +                .ToHandleChecked()); | 
| +  } | 
| + | 
| +  if (get_table) { | 
| +    DCHECK_LE(offset_table.size(), std::numeric_limits<int>::max()); | 
| +    Handle<FixedArray> offset_arr = isolate->factory()->NewFixedArray( | 
| +        static_cast<int>(offset_table.size())); | 
| +    int idx = 0; | 
| +    for (uint32_t elem : offset_table) { | 
| +      offset_arr->set(idx++, Smi::FromInt(elem)); | 
| +    } | 
| +    DCHECK(idx % 3 == 0); | 
| +    DCHECK_EQ(idx, offset_arr->length()); | 
| +    return_arr->set(1, *isolate->factory()->NewJSArrayWithElements(offset_arr)); | 
| +  } | 
| + | 
| +  return isolate->factory()->NewJSArrayWithElements(return_arr); | 
| +} | 
|  |