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); |
+} |