Commit 9983fda8 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[wasm] Introduce NamesProvider

NamesProvider class:
This consolidates logic used so far for the debugger interface.
It also adds support for the "extended name section" proposal:
https://github.com/WebAssembly/extended-name-section

StringBuilder class:
Like std::ostringstream, but 4x faster for this use case.

This lays the groundwork for an updated Wasm disassembler.

Bug: v8:12917
Change-Id: I98aa258147834bc0e314ba98c5927b4cd6070b8f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3720714Reviewed-by: 's avatarPhilip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81446}
parent 73ba62bc
...@@ -2512,6 +2512,8 @@ filegroup( ...@@ -2512,6 +2512,8 @@ filegroup(
"src/wasm/module-decoder.h", "src/wasm/module-decoder.h",
"src/wasm/module-instantiate.cc", "src/wasm/module-instantiate.cc",
"src/wasm/module-instantiate.h", "src/wasm/module-instantiate.h",
"src/wasm/names-provider.cc",
"src/wasm/names-provider.h",
"src/wasm/object-access.h", "src/wasm/object-access.h",
"src/wasm/signature-map.cc", "src/wasm/signature-map.cc",
"src/wasm/signature-map.h", "src/wasm/signature-map.h",
...@@ -2520,6 +2522,7 @@ filegroup( ...@@ -2520,6 +2522,7 @@ filegroup(
"src/wasm/stacks.h", "src/wasm/stacks.h",
"src/wasm/streaming-decoder.cc", "src/wasm/streaming-decoder.cc",
"src/wasm/streaming-decoder.h", "src/wasm/streaming-decoder.h",
"src/wasm/string-builder.h",
"src/wasm/struct-types.h", "src/wasm/struct-types.h",
"src/wasm/sync-streaming-decoder.cc", "src/wasm/sync-streaming-decoder.cc",
"src/wasm/value-type.cc", "src/wasm/value-type.cc",
......
...@@ -3610,15 +3610,18 @@ v8_header_set("v8_internal_headers") { ...@@ -3610,15 +3610,18 @@ v8_header_set("v8_internal_headers") {
"src/wasm/module-compiler.h", "src/wasm/module-compiler.h",
"src/wasm/module-decoder.h", "src/wasm/module-decoder.h",
"src/wasm/module-instantiate.h", "src/wasm/module-instantiate.h",
"src/wasm/names-provider.h",
"src/wasm/object-access.h", "src/wasm/object-access.h",
"src/wasm/signature-map.h", "src/wasm/signature-map.h",
"src/wasm/simd-shuffle.h", "src/wasm/simd-shuffle.h",
"src/wasm/stacks.h", "src/wasm/stacks.h",
"src/wasm/streaming-decoder.h", "src/wasm/streaming-decoder.h",
"src/wasm/string-builder.h",
"src/wasm/struct-types.h", "src/wasm/struct-types.h",
"src/wasm/value-type.h", "src/wasm/value-type.h",
"src/wasm/wasm-arguments.h", "src/wasm/wasm-arguments.h",
"src/wasm/wasm-code-manager.h", "src/wasm/wasm-code-manager.h",
"src/wasm/wasm-debug.h",
"src/wasm/wasm-engine.h", "src/wasm/wasm-engine.h",
"src/wasm/wasm-external-refs.h", "src/wasm/wasm-external-refs.h",
"src/wasm/wasm-feature-flags.h", "src/wasm/wasm-feature-flags.h",
...@@ -4694,6 +4697,7 @@ v8_source_set("v8_base_without_compiler") { ...@@ -4694,6 +4697,7 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/module-compiler.cc", "src/wasm/module-compiler.cc",
"src/wasm/module-decoder.cc", "src/wasm/module-decoder.cc",
"src/wasm/module-instantiate.cc", "src/wasm/module-instantiate.cc",
"src/wasm/names-provider.cc",
"src/wasm/signature-map.cc", "src/wasm/signature-map.cc",
"src/wasm/simd-shuffle.cc", "src/wasm/simd-shuffle.cc",
"src/wasm/streaming-decoder.cc", "src/wasm/streaming-decoder.cc",
...@@ -4701,7 +4705,6 @@ v8_source_set("v8_base_without_compiler") { ...@@ -4701,7 +4705,6 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/value-type.cc", "src/wasm/value-type.cc",
"src/wasm/wasm-code-manager.cc", "src/wasm/wasm-code-manager.cc",
"src/wasm/wasm-debug.cc", "src/wasm/wasm-debug.cc",
"src/wasm/wasm-debug.h",
"src/wasm/wasm-engine.cc", "src/wasm/wasm-engine.cc",
"src/wasm/wasm-external-refs.cc", "src/wasm/wasm-external-refs.cc",
"src/wasm/wasm-features.cc", "src/wasm/wasm-features.cc",
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "src/debug/debug-wasm-objects-inl.h" #include "src/debug/debug-wasm-objects-inl.h"
#include "src/execution/frames-inl.h" #include "src/execution/frames-inl.h"
#include "src/objects/property-descriptor.h" #include "src/objects/property-descriptor.h"
#include "src/wasm/names-provider.h"
#include "src/wasm/string-builder.h"
#include "src/wasm/wasm-debug.h" #include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-value.h" #include "src/wasm/wasm-value.h"
...@@ -18,53 +20,10 @@ namespace v8 { ...@@ -18,53 +20,10 @@ namespace v8 {
namespace internal { namespace internal {
namespace { namespace {
// Helper for unpacking a maybe name that makes a default with an index if using StringBuilder = wasm::StringBuilder;
// the name is empty. If the name is not empty, it's prefixed with a $. Handle<String> ToInternalString(StringBuilder& sb, Isolate* isolate) {
Handle<String> GetNameOrDefault(Isolate* isolate, return isolate->factory()->InternalizeString(
MaybeHandle<String> maybe_name, base::VectorOf(sb.start(), sb.length()));
const char* default_name_prefix,
uint32_t index) {
Handle<String> name;
if (maybe_name.ToHandle(&name)) {
name = isolate->factory()
->NewConsString(
isolate->factory()->NewStringFromAsciiChecked("$"), name)
.ToHandleChecked();
return isolate->factory()->InternalizeString(name);
}
base::EmbeddedVector<char, 64> value;
int len = SNPrintF(value, "%s%u", default_name_prefix, index);
return isolate->factory()->InternalizeString(value.SubVector(0, len));
}
MaybeHandle<String> GetNameFromImportsAndExportsOrNull(
Isolate* isolate, Handle<WasmInstanceObject> instance,
wasm::ImportExportKindCode kind, uint32_t index) {
auto debug_info = instance->module_object().native_module()->GetDebugInfo();
wasm::ModuleWireBytes wire_bytes(
instance->module_object().native_module()->wire_bytes());
auto import_name_ref = debug_info->GetImportName(kind, index);
if (!import_name_ref.first.is_empty()) {
base::ScopedVector<char> name(import_name_ref.first.length() + 1 +
import_name_ref.second.length());
auto name_begin = &name.first(), name_end = name_begin;
auto module_name = wire_bytes.GetNameOrNull(import_name_ref.first);
name_end = std::copy(module_name.begin(), module_name.end(), name_end);
*name_end++ = '.';
auto field_name = wire_bytes.GetNameOrNull(import_name_ref.second);
name_end = std::copy(field_name.begin(), field_name.end(), name_end);
return isolate->factory()->NewStringFromUtf8(
base::VectorOf(name_begin, name_end - name_begin));
}
auto export_name_ref = debug_info->GetExportName(kind, index);
if (!export_name_ref.is_empty()) {
auto name = wire_bytes.GetNameOrNull(export_name_ref);
return isolate->factory()->NewStringFromUtf8(name);
}
return {};
} }
enum DebugProxyId { enum DebugProxyId {
...@@ -352,12 +311,11 @@ struct GlobalsProxy : NamedDebugProxy<GlobalsProxy, kGlobalsProxy> { ...@@ -352,12 +311,11 @@ struct GlobalsProxy : NamedDebugProxy<GlobalsProxy, kGlobalsProxy> {
static Handle<String> GetName(Isolate* isolate, static Handle<String> GetName(Isolate* isolate,
Handle<WasmInstanceObject> instance, Handle<WasmInstanceObject> instance,
uint32_t index) { uint32_t index) {
return GetNameOrDefault( wasm::NamesProvider* names =
isolate, instance->module_object().native_module()->GetNamesProvider();
GetNameFromImportsAndExportsOrNull( StringBuilder sb;
isolate, instance, wasm::ImportExportKindCode::kExternalGlobal, names->PrintGlobalName(sb, index);
index), return ToInternalString(sb, isolate);
"$global", index);
} }
}; };
...@@ -378,12 +336,11 @@ struct MemoriesProxy : NamedDebugProxy<MemoriesProxy, kMemoriesProxy> { ...@@ -378,12 +336,11 @@ struct MemoriesProxy : NamedDebugProxy<MemoriesProxy, kMemoriesProxy> {
static Handle<String> GetName(Isolate* isolate, static Handle<String> GetName(Isolate* isolate,
Handle<WasmInstanceObject> instance, Handle<WasmInstanceObject> instance,
uint32_t index) { uint32_t index) {
return GetNameOrDefault( wasm::NamesProvider* names =
isolate, instance->module_object().native_module()->GetNamesProvider();
GetNameFromImportsAndExportsOrNull( StringBuilder sb;
isolate, instance, wasm::ImportExportKindCode::kExternalMemory, names->PrintMemoryName(sb, index);
index), return ToInternalString(sb, isolate);
"$memory", index);
} }
}; };
...@@ -404,12 +361,11 @@ struct TablesProxy : NamedDebugProxy<TablesProxy, kTablesProxy> { ...@@ -404,12 +361,11 @@ struct TablesProxy : NamedDebugProxy<TablesProxy, kTablesProxy> {
static Handle<String> GetName(Isolate* isolate, static Handle<String> GetName(Isolate* isolate,
Handle<WasmInstanceObject> instance, Handle<WasmInstanceObject> instance,
uint32_t index) { uint32_t index) {
return GetNameOrDefault( wasm::NamesProvider* names =
isolate, instance->module_object().native_module()->GetNamesProvider();
GetNameFromImportsAndExportsOrNull( StringBuilder sb;
isolate, instance, wasm::ImportExportKindCode::kExternalTable, names->PrintTableName(sb, index);
index), return ToInternalString(sb, isolate);
"$table", index);
} }
}; };
...@@ -454,14 +410,10 @@ struct LocalsProxy : NamedDebugProxy<LocalsProxy, kLocalsProxy, FixedArray> { ...@@ -454,14 +410,10 @@ struct LocalsProxy : NamedDebugProxy<LocalsProxy, kLocalsProxy, FixedArray> {
auto native_module = auto native_module =
WasmModuleObject::cast(values->get(count + 0)).native_module(); WasmModuleObject::cast(values->get(count + 0)).native_module();
auto function_index = Smi::ToInt(Smi::cast(values->get(count + 1))); auto function_index = Smi::ToInt(Smi::cast(values->get(count + 1)));
wasm::ModuleWireBytes module_wire_bytes(native_module->wire_bytes()); wasm::NamesProvider* names = native_module->GetNamesProvider();
auto name_vec = module_wire_bytes.GetNameOrNull( StringBuilder sb;
native_module->GetDebugInfo()->GetLocalName(function_index, index)); names->PrintLocalName(sb, function_index, index);
return GetNameOrDefault( return ToInternalString(sb, isolate);
isolate,
name_vec.empty() ? MaybeHandle<String>()
: isolate->factory()->NewStringFromUtf8(name_vec),
"$var", index);
} }
}; };
...@@ -772,22 +724,9 @@ Handle<String> WasmSimd128ToString(Isolate* isolate, wasm::Simd128 s128) { ...@@ -772,22 +724,9 @@ Handle<String> WasmSimd128ToString(Isolate* isolate, wasm::Simd128 s128) {
Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type, Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
wasm::NativeModule* module) { wasm::NativeModule* module) {
DCHECK(type.is_object_reference()); DCHECK(type.is_object_reference());
std::ostringstream name; StringBuilder name;
if (type.heap_type().is_generic()) { module->GetNamesProvider()->PrintValueType(name, type);
name << type.name(); return ToInternalString(name, isolate);
} else {
name << "(ref " << (type.is_nullable() ? "null " : "") << "$";
wasm::ModuleWireBytes module_wire_bytes(module->wire_bytes());
base::Vector<const char> module_name = module_wire_bytes.GetNameOrNull(
module->GetDebugInfo()->GetTypeName(type.ref_index()));
if (module_name.empty()) {
name << "type" << type.ref_index();
} else {
name.write(module_name.begin(), module_name.size());
}
name << ")";
}
return isolate->factory()->InternalizeString(base::VectorOf(name.str()));
} }
} // namespace } // namespace
...@@ -866,14 +805,10 @@ struct StructProxy : NamedDebugProxy<StructProxy, kStructProxy, FixedArray> { ...@@ -866,14 +805,10 @@ struct StructProxy : NamedDebugProxy<StructProxy, kStructProxy, FixedArray> {
wasm::NativeModule* native_module = wasm::NativeModule* native_module =
WasmModuleObject::cast(data->get(kModuleIndex)).native_module(); WasmModuleObject::cast(data->get(kModuleIndex)).native_module();
int struct_type_index = Smi::ToInt(Smi::cast(data->get(kTypeIndexIndex))); int struct_type_index = Smi::ToInt(Smi::cast(data->get(kTypeIndexIndex)));
wasm::ModuleWireBytes module_wire_bytes(native_module->wire_bytes()); wasm::NamesProvider* names = native_module->GetNamesProvider();
base::Vector<const char> name_vec = module_wire_bytes.GetNameOrNull( StringBuilder sb;
native_module->GetDebugInfo()->GetFieldName(struct_type_index, index)); names->PrintFieldName(sb, struct_type_index, index);
return GetNameOrDefault( return ToInternalString(sb, isolate);
isolate,
name_vec.empty() ? MaybeHandle<String>()
: isolate->factory()->NewStringFromUtf8(name_vec),
"$field", index);
} }
}; };
...@@ -1017,18 +952,14 @@ Handle<String> GetWasmFunctionDebugName(Isolate* isolate, ...@@ -1017,18 +952,14 @@ Handle<String> GetWasmFunctionDebugName(Isolate* isolate,
Handle<WasmInstanceObject> instance, Handle<WasmInstanceObject> instance,
uint32_t func_index) { uint32_t func_index) {
Handle<WasmModuleObject> module_object(instance->module_object(), isolate); Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
MaybeHandle<String> maybe_name = WasmModuleObject::GetFunctionNameOrNull( wasm::NamesProvider* names =
isolate, module_object, func_index); module_object->native_module()->GetNamesProvider();
if (module_object->is_asm_js()) { StringBuilder sb;
// In case of asm.js, we use the names from the function declarations. wasm::NamesProvider::FunctionNamesBehavior behavior =
return maybe_name.ToHandleChecked(); module_object->is_asm_js() ? wasm::NamesProvider::kWasmInternal
} : wasm::NamesProvider::kDevTools;
if (maybe_name.is_null()) { names->PrintFunctionName(sb, func_index, behavior);
maybe_name = GetNameFromImportsAndExportsOrNull( return ToInternalString(sb, isolate);
isolate, instance, wasm::ImportExportKindCode::kExternalFunction,
func_index);
}
return GetNameOrDefault(isolate, maybe_name, "$func", func_index);
} }
Handle<ArrayList> AddWasmInstanceObjectInternalProperties( Handle<ArrayList> AddWasmInstanceObjectInternalProperties(
......
...@@ -1274,6 +1274,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1274,6 +1274,8 @@ class ModuleDecoderImpl : public Decoder {
// Ignore all but the first occurrence of name section. // Ignore all but the first occurrence of name section.
if (!has_seen_unordered_section(kNameSectionCode)) { if (!has_seen_unordered_section(kNameSectionCode)) {
set_seen_unordered_section(kNameSectionCode); set_seen_unordered_section(kNameSectionCode);
module_->name_section = {buffer_offset_,
static_cast<uint32_t>(end_ - start_)};
// Use an inner decoder so that errors don't fail the outer decoder. // Use an inner decoder so that errors don't fail the outer decoder.
Decoder inner(start_, pc_, end_, buffer_offset_); Decoder inner(start_, pc_, end_, buffer_offset_);
// Decode all name subsections. // Decode all name subsections.
...@@ -1744,9 +1746,9 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1744,9 +1746,9 @@ class ModuleDecoderImpl : public Decoder {
void VerifyFunctionBody(AccountingAllocator* allocator, uint32_t func_num, void VerifyFunctionBody(AccountingAllocator* allocator, uint32_t func_num,
const ModuleWireBytes& wire_bytes, const ModuleWireBytes& wire_bytes,
const WasmModule* module, WasmFunction* function) { const WasmModule* module, WasmFunction* function) {
WasmFunctionName func_name(function,
wire_bytes.GetNameOrNull(function, module));
if (FLAG_trace_wasm_decoder) { if (FLAG_trace_wasm_decoder) {
WasmFunctionName func_name(function,
wire_bytes.GetNameOrNull(function, module));
StdoutStream{} << "Verifying wasm function " << func_name << std::endl; StdoutStream{} << "Verifying wasm function " << func_name << std::endl;
} }
FunctionBody body = { FunctionBody body = {
...@@ -1762,6 +1764,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1762,6 +1764,8 @@ class ModuleDecoderImpl : public Decoder {
// location. // location.
if (result.failed() && intermediate_error_.empty()) { if (result.failed() && intermediate_error_.empty()) {
// Wrap the error message from the function decoder. // Wrap the error message from the function decoder.
WasmFunctionName func_name(function,
wire_bytes.GetNameOrNull(function, module));
std::ostringstream error_msg; std::ostringstream error_msg;
error_msg << "in function " << func_name << ": " error_msg << "in function " << func_name << ": "
<< result.error().message(); << result.error().message();
...@@ -2561,10 +2565,7 @@ bool FindNameSection(Decoder* decoder) { ...@@ -2561,10 +2565,7 @@ bool FindNameSection(Decoder* decoder) {
} // namespace } // namespace
void DecodeFunctionNames(const byte* module_start, const byte* module_end, void DecodeFunctionNames(const byte* module_start, const byte* module_end,
std::unordered_map<uint32_t, WireBytesRef>* names) { std::unordered_map<uint32_t, WireBytesRef>& names) {
DCHECK_NOT_NULL(names);
DCHECK(names->empty());
Decoder decoder(module_start, module_end); Decoder decoder(module_start, module_end);
if (FindNameSection(&decoder)) { if (FindNameSection(&decoder)) {
while (decoder.ok() && decoder.more()) { while (decoder.ok() && decoder.more()) {
...@@ -2589,52 +2590,71 @@ void DecodeFunctionNames(const byte* module_start, const byte* module_end, ...@@ -2589,52 +2590,71 @@ void DecodeFunctionNames(const byte* module_start, const byte* module_end,
// You can even assign to the same function multiple times (last valid // You can even assign to the same function multiple times (last valid
// one wins). // one wins).
if (decoder.ok() && validate_utf8(&decoder, name)) { if (decoder.ok() && validate_utf8(&decoder, name)) {
names->insert(std::make_pair(function_index, name)); names.insert(std::make_pair(function_index, name));
} }
} }
} }
} }
} }
NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes, namespace {
uint8_t name_section_kind) {
Decoder decoder(module_bytes);
if (!FindNameSection(&decoder)) return NameMap{{}};
void DecodeNameMap(NameMap& target, Decoder& decoder) {
std::vector<NameAssoc> names; std::vector<NameAssoc> names;
while (decoder.ok() && decoder.more()) { uint32_t count = decoder.consume_u32v("names count");
uint8_t name_type = decoder.consume_u8("name type"); names.reserve(count);
if (name_type & 0x80) break; // no varuint7 for (uint32_t i = 0; i < count; i++) {
uint32_t index = decoder.consume_u32v("index");
uint32_t name_payload_len = decoder.consume_u32v("name payload length"); WireBytesRef name =
if (!decoder.checkAvailable(name_payload_len)) break; consume_string(&decoder, StringValidation::kNone, "name");
if (!decoder.ok()) break;
if (name_type != name_section_kind) { if (index > kMaxInt) continue;
decoder.consume_bytes(name_payload_len, "name subsection payload"); if (name.is_empty()) continue; // Empty names are useless.
continue; if (!validate_utf8(&decoder, name)) continue;
} names.emplace_back(static_cast<int>(index), name);
}
std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{});
target = NameMap{std::move(names)};
}
uint32_t count = decoder.consume_u32v("names count"); void DecodeIndirectNameMap(IndirectNameMap& target, Decoder& decoder) {
for (uint32_t i = 0; i < count; i++) { std::vector<IndirectNameMapEntry> entries;
uint32_t index = decoder.consume_u32v("index"); uint32_t outer_count = decoder.consume_u32v("outer count");
entries.reserve(outer_count);
for (uint32_t i = 0; i < outer_count; ++i) {
uint32_t outer_index = decoder.consume_u32v("outer index");
if (outer_index > kMaxInt) continue;
std::vector<NameAssoc> names;
uint32_t inner_count = decoder.consume_u32v("inner count");
names.reserve(inner_count);
for (uint32_t k = 0; k < inner_count; ++k) {
uint32_t inner_index = decoder.consume_u32v("inner index");
WireBytesRef name = WireBytesRef name =
consume_string(&decoder, StringValidation::kNone, "name"); consume_string(&decoder, StringValidation::kNone, "name");
if (!decoder.ok()) break; if (!decoder.ok()) break;
if (index > kMaxInt) continue; if (inner_index > kMaxInt) continue;
if (name.is_empty()) continue; // Empty names are useless.
if (!validate_utf8(&decoder, name)) continue; if (!validate_utf8(&decoder, name)) continue;
names.emplace_back(static_cast<int>(index), name); names.emplace_back(static_cast<int>(inner_index), name);
} }
// Use stable sort to get deterministic names (the first one declared)
// even in the presence of duplicates.
std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{});
entries.emplace_back(static_cast<int>(outer_index), std::move(names));
} }
std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{}); std::stable_sort(entries.begin(), entries.end(),
return NameMap{std::move(names)}; IndirectNameMapEntry::IndexLess{});
target = IndirectNameMap{std::move(entries)};
} }
IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes, } // namespace
uint8_t name_section_kind) {
Decoder decoder(module_bytes);
if (!FindNameSection(&decoder)) return IndirectNameMap{{}};
std::vector<IndirectNameMapEntry> entries; DecodedNameSection::DecodedNameSection(base::Vector<const uint8_t> wire_bytes,
WireBytesRef name_section) {
if (name_section.is_empty()) return; // No name section.
Decoder decoder(wire_bytes.begin() + name_section.offset(),
wire_bytes.begin() + name_section.end_offset(),
name_section.offset());
while (decoder.ok() && decoder.more()) { while (decoder.ok() && decoder.more()) {
uint8_t name_type = decoder.consume_u8("name type"); uint8_t name_type = decoder.consume_u8("name type");
if (name_type & 0x80) break; // no varuint7 if (name_type & 0x80) break; // no varuint7
...@@ -2642,36 +2662,44 @@ IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes, ...@@ -2642,36 +2662,44 @@ IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes,
uint32_t name_payload_len = decoder.consume_u32v("name payload length"); uint32_t name_payload_len = decoder.consume_u32v("name payload length");
if (!decoder.checkAvailable(name_payload_len)) break; if (!decoder.checkAvailable(name_payload_len)) break;
if (name_type != name_section_kind) { switch (name_type) {
decoder.consume_bytes(name_payload_len, "name subsection payload"); case kModuleCode:
continue; case kFunctionCode:
} // Already handled elsewhere.
decoder.consume_bytes(name_payload_len);
uint32_t outer_count = decoder.consume_u32v("outer count"); break;
for (uint32_t i = 0; i < outer_count; ++i) { case kLocalCode:
uint32_t outer_index = decoder.consume_u32v("outer index"); DecodeIndirectNameMap(local_names_, decoder);
if (outer_index > kMaxInt) continue; break;
std::vector<NameAssoc> names; case kLabelCode:
uint32_t inner_count = decoder.consume_u32v("inner count"); DecodeIndirectNameMap(label_names_, decoder);
for (uint32_t k = 0; k < inner_count; ++k) { break;
uint32_t inner_index = decoder.consume_u32v("inner index"); case kTypeCode:
WireBytesRef name = DecodeNameMap(type_names_, decoder);
consume_string(&decoder, StringValidation::kNone, "name"); break;
if (!decoder.ok()) break; case kTableCode:
if (inner_index > kMaxInt) continue; DecodeNameMap(table_names_, decoder);
// Ignore non-utf8 names. break;
if (!validate_utf8(&decoder, name)) continue; case kMemoryCode:
names.emplace_back(static_cast<int>(inner_index), name); DecodeNameMap(memory_names_, decoder);
} break;
// Use stable sort to get deterministic names (the first one declared) case kGlobalCode:
// even in the presence of duplicates. DecodeNameMap(global_names_, decoder);
std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{}); break;
entries.emplace_back(static_cast<int>(outer_index), std::move(names)); case kElementSegmentCode:
DecodeNameMap(element_segment_names_, decoder);
break;
case kDataSegmentCode:
DecodeNameMap(data_segment_names_, decoder);
break;
case kFieldCode:
DecodeIndirectNameMap(field_names_, decoder);
break;
case kTagCode:
DecodeNameMap(tag_names_, decoder);
break;
} }
} }
std::stable_sort(entries.begin(), entries.end(),
IndirectNameMapEntry::IndexLess{});
return IndirectNameMap{std::move(entries)};
} }
#undef TRACE #undef TRACE
......
...@@ -80,7 +80,7 @@ class NameAssoc { ...@@ -80,7 +80,7 @@ class NameAssoc {
class NameMap { class NameMap {
public: public:
// For performance reasons, {NameMap} should not be copied. // For performance reasons, {NameMap} should not be copied.
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(NameMap); MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(NameMap);
explicit NameMap(std::vector<NameAssoc> names) : names_(std::move(names)) { explicit NameMap(std::vector<NameAssoc> names) : names_(std::move(names)) {
DCHECK( DCHECK(
...@@ -123,7 +123,7 @@ class IndirectNameMapEntry : public NameMap { ...@@ -123,7 +123,7 @@ class IndirectNameMapEntry : public NameMap {
class IndirectNameMap { class IndirectNameMap {
public: public:
// For performance reasons, {IndirectNameMap} should not be copied. // For performance reasons, {IndirectNameMap} should not be copied.
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(IndirectNameMap); MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(IndirectNameMap);
explicit IndirectNameMap(std::vector<IndirectNameMapEntry> functions) explicit IndirectNameMap(std::vector<IndirectNameMapEntry> functions)
: functions_(std::move(functions)) { : functions_(std::move(functions)) {
...@@ -144,6 +144,26 @@ class IndirectNameMap { ...@@ -144,6 +144,26 @@ class IndirectNameMap {
std::vector<IndirectNameMapEntry> functions_; std::vector<IndirectNameMapEntry> functions_;
}; };
class DecodedNameSection {
public:
explicit DecodedNameSection(base::Vector<const uint8_t> wire_bytes,
WireBytesRef name_section);
private:
friend class NamesProvider;
IndirectNameMap local_names_;
IndirectNameMap label_names_;
NameMap type_names_;
NameMap table_names_;
NameMap memory_names_;
NameMap global_names_;
NameMap element_segment_names_;
NameMap data_segment_names_;
IndirectNameMap field_names_;
NameMap tag_names_;
};
enum class DecodingMethod { enum class DecodingMethod {
kSync, kSync,
kAsync, kAsync,
...@@ -195,16 +215,7 @@ AsmJsOffsetsResult DecodeAsmJsOffsets( ...@@ -195,16 +215,7 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(
// unordered map. Only names with valid utf8 encoding are stored and conflicts // unordered map. Only names with valid utf8 encoding are stored and conflicts
// are resolved by choosing the last name read. // are resolved by choosing the last name read.
void DecodeFunctionNames(const byte* module_start, const byte* module_end, void DecodeFunctionNames(const byte* module_start, const byte* module_end,
std::unordered_map<uint32_t, WireBytesRef>* names); std::unordered_map<uint32_t, WireBytesRef>& names);
// Decode the requested subsection of the name section.
// The result will be empty if no name section is present. On encountering an
// error in the name section, returns all information decoded up to the first
// error.
NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes,
uint8_t name_section_kind);
IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes,
uint8_t name_section_kind);
class ModuleDecoderImpl; class ModuleDecoderImpl;
......
// Copyright 2022 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/names-provider.h"
#include "src/strings/unicode-decoder.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/string-builder.h"
namespace v8 {
namespace internal {
namespace wasm {
NamesProvider::NamesProvider(const WasmModule* module,
base::Vector<const uint8_t> wire_bytes)
: module_(module), wire_bytes_(wire_bytes) {}
NamesProvider::~NamesProvider() = default;
void NamesProvider::DecodeNamesIfNotYetDone() {
base::MutexGuard lock(&mutex_);
if (has_decoded_) return;
has_decoded_ = true;
name_section_names_.reset(
new DecodedNameSection(wire_bytes_, module_->name_section));
ComputeNamesFromImportsExports();
}
// Function names are generally handled separately from other names; in
// particular we support decoding function names without decoding any other
// names, in which case also computing fallback names from imports and exports
// must happen separately.
void NamesProvider::ComputeFunctionNamesFromImportsExports() {
DCHECK(!has_computed_function_import_names_);
has_computed_function_import_names_ = true;
for (const WasmImport& import : module_->import_table) {
if (import.kind != kExternalFunction) continue;
if (module_->lazily_generated_names.Has(import.index)) continue;
ComputeImportName(import, import_export_function_names_);
}
for (const WasmExport& ex : module_->export_table) {
if (ex.kind != kExternalFunction) continue;
if (module_->lazily_generated_names.Has(ex.index)) continue;
ComputeExportName(ex, import_export_function_names_);
}
}
void NamesProvider::ComputeNamesFromImportsExports() {
DCHECK(!has_computed_import_names_);
has_computed_import_names_ = true;
DCHECK(has_decoded_);
for (const WasmImport import : module_->import_table) {
switch (import.kind) {
case kExternalFunction:
continue; // Functions are handled separately.
case kExternalTable:
if (name_section_names_->table_names_.GetName(import.index).is_set()) {
continue;
}
ComputeImportName(import, import_export_table_names_);
break;
case kExternalMemory:
if (name_section_names_->memory_names_.GetName(import.index).is_set()) {
continue;
}
ComputeImportName(import, import_export_memory_names_);
break;
case kExternalGlobal:
if (name_section_names_->global_names_.GetName(import.index).is_set()) {
continue;
}
ComputeImportName(import, import_export_global_names_);
break;
case kExternalTag:
if (name_section_names_->tag_names_.GetName(import.index).is_set()) {
continue;
}
ComputeImportName(import, import_export_tag_names_);
break;
}
}
for (const WasmExport& ex : module_->export_table) {
switch (ex.kind) {
case kExternalFunction:
continue; // Functions are handled separately.
case kExternalTable:
if (name_section_names_->table_names_.GetName(ex.index).is_set()) {
continue;
}
ComputeExportName(ex, import_export_table_names_);
break;
case kExternalMemory:
if (name_section_names_->memory_names_.GetName(ex.index).is_set()) {
continue;
}
ComputeExportName(ex, import_export_memory_names_);
break;
case kExternalGlobal:
if (name_section_names_->global_names_.GetName(ex.index).is_set()) {
continue;
}
ComputeExportName(ex, import_export_global_names_);
break;
case kExternalTag:
if (name_section_names_->tag_names_.GetName(ex.index).is_set()) {
continue;
}
ComputeExportName(ex, import_export_tag_names_);
break;
}
}
}
namespace {
// Any disallowed characters get replaced with '_'. Reference:
// https://webassembly.github.io/spec/core/text/values.html#text-id
static constexpr char kIdentifierChar[] = {
'_', '!', '_', '#', '$', '%', '&', '\'', // --
'_', '_', '*', '+', '_', '-', '.', '/', // --
'0', '1', '2', '3', '4', '5', '6', '7', // --
'8', '9', ':', '_', '<', '=', '>', '?', // --
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', // --
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // --
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', // --
'X', 'Y', 'Z', '_', '\\', '_', '^', '_', // --
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', // --
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // --
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', // --
'x', 'y', 'z', '_', '|', '_', '~', '_', // --
};
// To match legacy wasmparser behavior, we emit one '_' per invalid UTF16
// code unit.
// We could decide that we don't care much how exactly non-ASCII names are
// rendered and simplify this to "one '_' per invalid UTF8 byte".
void SanitizeUnicodeName(StringBuilder& out, const byte* utf8_src,
size_t length) {
base::Vector<const uint8_t> utf8_data(utf8_src, length);
Utf8Decoder decoder(utf8_data);
std::vector<uint16_t> utf16(decoder.utf16_length());
decoder.Decode(utf16.data(), utf8_data);
for (uint16_t c : utf16) {
if (c < 32 || c >= 127) {
out << '_';
} else {
out << kIdentifierChar[c - 32];
}
}
}
} // namespace
void NamesProvider::ComputeImportName(const WasmImport& import,
std::map<uint32_t, std::string>& target) {
const byte* mod_start = wire_bytes_.begin() + import.module_name.offset();
size_t mod_length = import.module_name.length();
const byte* field_start = wire_bytes_.begin() + import.field_name.offset();
size_t field_length = import.field_name.length();
StringBuilder buffer;
buffer << '$';
SanitizeUnicodeName(buffer, mod_start, mod_length);
buffer << '.';
SanitizeUnicodeName(buffer, field_start, field_length);
target[import.index] = std::string(buffer.start(), buffer.length());
}
void NamesProvider::ComputeExportName(const WasmExport& ex,
std::map<uint32_t, std::string>& target) {
if (target.find(ex.index) != target.end()) return;
size_t length = ex.name.length();
if (length == 0) return;
StringBuilder buffer;
buffer << '$';
SanitizeUnicodeName(buffer, wire_bytes_.begin() + ex.name.offset(), length);
target[ex.index] = std::string(buffer.start(), buffer.length());
}
namespace {
V8_INLINE void MaybeAddComment(StringBuilder& out, uint32_t index,
bool add_comment) {
if (add_comment) out << " (;" << index << ";)";
}
} // namespace
void NamesProvider::WriteRef(StringBuilder& out, WireBytesRef ref) {
out.write(wire_bytes_.begin() + ref.offset(), ref.length());
}
void NamesProvider::PrintFunctionName(StringBuilder& out,
uint32_t function_index,
FunctionNamesBehavior behavior,
IndexAsComment index_as_comment) {
// Function names are stored elsewhere, because we need to access them
// during (streaming) compilation when the NamesProvider isn't ready yet.
WireBytesRef ref = module_->lazily_generated_names.LookupFunctionName(
ModuleWireBytes(wire_bytes_), function_index);
if (ref.is_set()) {
if (behavior == kDevTools) {
out << '$';
WriteRef(out, ref);
MaybeAddComment(out, function_index, index_as_comment);
} else {
// For kWasmInternal behavior, function names don't get a `$` prefix.
WriteRef(out, ref);
}
return;
}
if (behavior == kWasmInternal) return;
{
base::MutexGuard lock(&mutex_);
if (!has_computed_function_import_names_) {
ComputeFunctionNamesFromImportsExports();
}
}
auto it = import_export_function_names_.find(function_index);
if (it != import_export_function_names_.end()) {
out << it->second;
MaybeAddComment(out, function_index, index_as_comment);
} else {
out << "$func" << function_index;
}
}
void NamesProvider::PrintLocalName(StringBuilder& out, uint32_t function_index,
uint32_t local_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref =
name_section_names_->local_names_.GetName(function_index, local_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
MaybeAddComment(out, local_index, index_as_comment);
} else {
out << "$var" << local_index;
}
}
void NamesProvider::PrintLabelName(StringBuilder& out, uint32_t function_index,
uint32_t label_index,
uint32_t fallback_index) {
DecodeNamesIfNotYetDone();
WireBytesRef ref =
name_section_names_->label_names_.GetName(function_index, label_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
} else {
out << "$label" << fallback_index;
}
}
void NamesProvider::PrintTypeName(StringBuilder& out, uint32_t type_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref = name_section_names_->type_names_.GetName(type_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
return MaybeAddComment(out, type_index, index_as_comment);
}
out << "$type" << type_index;
}
void NamesProvider::PrintTableName(StringBuilder& out, uint32_t table_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref = name_section_names_->table_names_.GetName(table_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
return MaybeAddComment(out, table_index, index_as_comment);
}
auto it = import_export_table_names_.find(table_index);
if (it != import_export_table_names_.end()) {
out << it->second;
return MaybeAddComment(out, table_index, index_as_comment);
}
out << "$table" << table_index;
}
void NamesProvider::PrintMemoryName(StringBuilder& out, uint32_t memory_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref = name_section_names_->memory_names_.GetName(memory_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
return MaybeAddComment(out, memory_index, index_as_comment);
}
auto it = import_export_memory_names_.find(memory_index);
if (it != import_export_memory_names_.end()) {
out << it->second;
return MaybeAddComment(out, memory_index, index_as_comment);
}
out << "$memory" << memory_index;
}
void NamesProvider::PrintGlobalName(StringBuilder& out, uint32_t global_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref = name_section_names_->global_names_.GetName(global_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
return MaybeAddComment(out, global_index, index_as_comment);
}
auto it = import_export_global_names_.find(global_index);
if (it != import_export_global_names_.end()) {
out << it->second;
return MaybeAddComment(out, global_index, index_as_comment);
}
out << "$global" << global_index;
}
void NamesProvider::PrintElementSegmentName(StringBuilder& out,
uint32_t element_segment_index) {
DecodeNamesIfNotYetDone();
WireBytesRef ref = name_section_names_->element_segment_names_.GetName(
element_segment_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
} else {
out << "$elem" << element_segment_index;
}
}
void NamesProvider::PrintDataSegmentName(StringBuilder& out,
uint32_t data_segment_index) {
DecodeNamesIfNotYetDone();
WireBytesRef ref =
name_section_names_->data_segment_names_.GetName(data_segment_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
} else {
out << "$data" << data_segment_index;
}
}
void NamesProvider::PrintFieldName(StringBuilder& out, uint32_t struct_index,
uint32_t field_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref =
name_section_names_->field_names_.GetName(struct_index, field_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
return MaybeAddComment(out, field_index, index_as_comment);
}
out << "$field" << field_index;
}
void NamesProvider::PrintTagName(StringBuilder& out, uint32_t tag_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref = name_section_names_->tag_names_.GetName(tag_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
return MaybeAddComment(out, tag_index, index_as_comment);
}
out << "$tag" << tag_index;
}
void NamesProvider::PrintHeapType(StringBuilder& out, HeapType type) {
if (type.is_index()) {
PrintTypeName(out, type.ref_index());
} else {
out << type.name();
}
}
void NamesProvider::PrintValueType(StringBuilder& out, ValueType type) {
switch (type.kind()) {
case kRef:
case kOptRef:
if (type.encoding_needs_heap_type()) {
out << (type.kind() == kRef ? "(ref " : "(ref null ");
PrintHeapType(out, type.heap_type());
out << ')';
} else {
out << type.heap_type().name() << "ref";
}
break;
case kRtt:
out << "(rtt ";
PrintTypeName(out, type.ref_index());
out << ')';
break;
default:
out << wasm::name(type.kind());
}
}
} // namespace wasm
} // namespace internal
} // namespace v8
// Copyright 2022 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.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_WASM_NAMES_PROVIDER_H_
#define V8_WASM_NAMES_PROVIDER_H_
#include <map>
#include <string>
#include "src/base/vector.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
namespace wasm {
class DecodedNameSection;
class StringBuilder;
class NamesProvider {
public:
// {kWasmInternal}: only return raw name from name section.
// {kDevTools}: prepend '$', use import/export names as fallback,
// or "$funcN" as default.
enum FunctionNamesBehavior : bool { kWasmInternal = false, kDevTools = true };
enum IndexAsComment : bool {
kDontPrintIndex = false,
kIndexAsComment = true
};
NamesProvider(const WasmModule* module,
base::Vector<const uint8_t> wire_bytes);
~NamesProvider();
// Returns {false} if {devtools_behavior} == false and no name for
// {function_index} was present in the name section.
void PrintFunctionName(StringBuilder& out, uint32_t function_index,
FunctionNamesBehavior behavior = kWasmInternal,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintLocalName(StringBuilder& out, uint32_t function_index,
uint32_t local_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintLabelName(StringBuilder& out, uint32_t function_index,
uint32_t label_index, uint32_t fallback_index);
void PrintTypeName(StringBuilder& out, uint32_t type_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintTableName(StringBuilder& out, uint32_t table_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintMemoryName(StringBuilder& out, uint32_t memory_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintGlobalName(StringBuilder& out, uint32_t global_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintElementSegmentName(StringBuilder& out,
uint32_t element_segment_index);
void PrintDataSegmentName(StringBuilder& out, uint32_t data_segment_index);
void PrintFieldName(StringBuilder& out, uint32_t struct_index,
uint32_t field_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintTagName(StringBuilder& out, uint32_t tag_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintHeapType(StringBuilder& out, HeapType type);
void PrintValueType(StringBuilder& out, ValueType type);
private:
void DecodeNamesIfNotYetDone();
void ComputeFunctionNamesFromImportsExports();
void ComputeNamesFromImportsExports();
void ComputeImportName(const WasmImport& import,
std::map<uint32_t, std::string>& target);
void ComputeExportName(const WasmExport& ex,
std::map<uint32_t, std::string>& target);
void WriteRef(StringBuilder& out, WireBytesRef ref);
// Lazy loading must guard against concurrent modifications from multiple
// {WasmModuleObject}s.
base::Mutex mutex_;
bool has_decoded_{false};
bool has_computed_function_import_names_{false};
bool has_computed_import_names_{false};
const WasmModule* module_;
base::Vector<const uint8_t> wire_bytes_;
std::unique_ptr<DecodedNameSection> name_section_names_{};
std::map<uint32_t, std::string> import_export_function_names_;
std::map<uint32_t, std::string> import_export_table_names_;
std::map<uint32_t, std::string> import_export_memory_names_;
std::map<uint32_t, std::string> import_export_global_names_;
std::map<uint32_t, std::string> import_export_tag_names_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_NAMES_PROVIDER_H_
// Copyright 2022 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.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_WASM_STRING_BUILDER_H_
#define V8_WASM_STRING_BUILDER_H_
#include <cstring>
#include <string>
#include <vector>
#include "src/common/globals.h"
namespace v8 {
namespace internal {
namespace wasm {
// Similar to std::ostringstream, but about 4x faster.
// This base class works best for small-ish strings (up to kChunkSize); for
// producing large amounts of text, you probably want a subclass like
// MultiLineStringBuilder.
class StringBuilder {
public:
explicit StringBuilder() : on_growth_(kReplacePreviousChunk) {}
explicit StringBuilder(const StringBuilder&) = delete;
StringBuilder& operator=(const StringBuilder&) = delete;
~StringBuilder() {
for (char* chunk : chunks_) delete[] chunk;
}
// Reserves space for {n} characters and returns a pointer to its beginning.
// Clients *must* write all {n} characters after calling this!
// Don't call this directly, use operator<< overloads instead.
char* write(size_t n) {
if (remaining_bytes_ < n) Grow();
char* result = cursor_;
cursor_ += n;
remaining_bytes_ -= n;
return result;
}
// Convenience wrappers.
void write(const byte* data, size_t n) {
char* ptr = write(n);
memcpy(ptr, data, n);
}
void write(const char* data, size_t n) {
char* ptr = write(n);
memcpy(ptr, data, n);
}
const char* start() const { return start_; }
const char* cursor() const { return cursor_; }
size_t length() const { return static_cast<size_t>(cursor_ - start_); }
protected:
enum OnGrowth : bool { kKeepOldChunks, kReplacePreviousChunk };
// Useful for subclasses that divide the text into ranges, e.g. lines.
explicit StringBuilder(OnGrowth on_growth) : on_growth_(on_growth) {}
void start_here() { start_ = cursor_; }
void rewind_to_start() {
remaining_bytes_ += length();
cursor_ = start_;
}
private:
void Grow() {
size_t used = length();
// Safety net for super-long strings/lines.
size_t chunk_size = used < kChunkSize ? kChunkSize : used * 2;
char* new_chunk = new char[chunk_size];
memcpy(new_chunk, start_, used);
if (on_growth_ == kKeepOldChunks) {
chunks_.push_back(new_chunk);
}
start_ = new_chunk;
cursor_ = new_chunk + used;
remaining_bytes_ = chunk_size - used;
}
// Start small, to be cheap for the common case.
static constexpr size_t kStackSize = 256;
// If we have to grow, grow in big steps.
static constexpr size_t kChunkSize = 1024 * 1024;
char stack_buffer_[kStackSize];
std::vector<char*> chunks_; // A very simple Zone, essentially.
char* start_ = stack_buffer_;
char* cursor_ = stack_buffer_;
size_t remaining_bytes_ = kStackSize;
const OnGrowth on_growth_;
};
inline StringBuilder& operator<<(StringBuilder& sb, const char* str) {
size_t len = strlen(str);
char* ptr = sb.write(len);
memcpy(ptr, str, len);
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, char c) {
*sb.write(1) = c;
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, const std::string& s) {
sb.write(s.data(), s.length());
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, uint32_t n) {
if (n == 0) {
*sb.write(1) = '0';
return sb;
}
static constexpr size_t kBufferSize = 10; // Just enough for a uint32.
char buffer[kBufferSize];
char* end = buffer + kBufferSize;
char* out = end;
while (n != 0) {
*(--out) = '0' + (n % 10);
n /= 10;
}
sb.write(out, static_cast<size_t>(end - out));
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, int value) {
if (value >= 0) {
sb << static_cast<uint32_t>(value);
} else {
sb << "-" << ((~static_cast<uint32_t>(value)) + 1);
}
return sb;
}
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_STRING_BUILDER_H_
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "src/wasm/jump-table-assembler.h" #include "src/wasm/jump-table-assembler.h"
#include "src/wasm/memory-protection-key.h" #include "src/wasm/memory-protection-key.h"
#include "src/wasm/module-compiler.h" #include "src/wasm/module-compiler.h"
#include "src/wasm/names-provider.h"
#include "src/wasm/wasm-debug.h" #include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-import-wrapper-cache.h" #include "src/wasm/wasm-import-wrapper-cache.h"
...@@ -2503,6 +2504,16 @@ DebugInfo* NativeModule::GetDebugInfo() { ...@@ -2503,6 +2504,16 @@ DebugInfo* NativeModule::GetDebugInfo() {
return debug_info_.get(); return debug_info_.get();
} }
NamesProvider* NativeModule::GetNamesProvider() {
DCHECK(HasWireBytes());
base::RecursiveMutexGuard guard(&allocation_mutex_);
if (!names_provider_) {
names_provider_ =
std::make_unique<NamesProvider>(module_.get(), wire_bytes());
}
return names_provider_.get();
}
void WasmCodeManager::FreeNativeModule( void WasmCodeManager::FreeNativeModule(
base::Vector<VirtualMemory> owned_code_space, size_t committed_size) { base::Vector<VirtualMemory> owned_code_space, size_t committed_size) {
base::MutexGuard lock(&native_modules_mutex_); base::MutexGuard lock(&native_modules_mutex_);
......
...@@ -42,6 +42,7 @@ class Isolate; ...@@ -42,6 +42,7 @@ class Isolate;
namespace wasm { namespace wasm {
class DebugInfo; class DebugInfo;
class NamesProvider;
class NativeModule; class NativeModule;
struct WasmCompilationResult; struct WasmCompilationResult;
class WasmEngine; class WasmEngine;
...@@ -847,6 +848,9 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -847,6 +848,9 @@ class V8_EXPORT_PRIVATE NativeModule final {
// Get or create the debug info for this NativeModule. // Get or create the debug info for this NativeModule.
DebugInfo* GetDebugInfo(); DebugInfo* GetDebugInfo();
// Get or create the NamesProvider. Requires {HasWireBytes()}.
NamesProvider* GetNamesProvider();
uint32_t* tiering_budget_array() { return tiering_budgets_.get(); } uint32_t* tiering_budget_array() { return tiering_budgets_.get(); }
Counters* counters() const { return code_allocator_.counters(); } Counters* counters() const { return code_allocator_.counters(); }
...@@ -990,6 +994,8 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -990,6 +994,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
// mutex. // mutex.
std::unique_ptr<DebugInfo> debug_info_; std::unique_ptr<DebugInfo> debug_info_;
std::unique_ptr<NamesProvider> names_provider_;
TieringState tiering_state_ = kTieredUp; TieringState tiering_state_ = kTieredUp;
// Cache both baseline and top-tier code if we are debugging, to speed up // Cache both baseline and top-tier code if we are debugging, to speed up
......
...@@ -143,7 +143,9 @@ enum NameSectionKindCode : uint8_t { ...@@ -143,7 +143,9 @@ enum NameSectionKindCode : uint8_t {
kElementSegmentCode = 8, kElementSegmentCode = 8,
kDataSegmentCode = 9, kDataSegmentCode = 9,
// https://github.com/WebAssembly/gc/issues/193 // https://github.com/WebAssembly/gc/issues/193
kFieldCode = 10 kFieldCode = 10,
// https://github.com/WebAssembly/exception-handling/pull/213
kTagCode = 11,
}; };
// What to do when treating a stringref as WTF-8 and we see an isolated // What to do when treating a stringref as WTF-8 and we see an isolated
......
...@@ -157,66 +157,6 @@ class DebugInfoImpl { ...@@ -157,66 +157,6 @@ class DebugInfoImpl {
return module->functions[scope.code->index()]; return module->functions[scope.code->index()];
} }
WireBytesRef GetExportName(ImportExportKindCode kind, uint32_t index) {
base::MutexGuard guard(&mutex_);
if (!export_names_) {
export_names_ =
std::make_unique<std::map<ImportExportKey, WireBytesRef>>();
for (auto exp : native_module_->module()->export_table) {
auto exp_key = std::make_pair(exp.kind, exp.index);
if (export_names_->find(exp_key) != export_names_->end()) continue;
export_names_->insert(std::make_pair(exp_key, exp.name));
}
}
auto it = export_names_->find(std::make_pair(kind, index));
if (it != export_names_->end()) return it->second;
return {};
}
std::pair<WireBytesRef, WireBytesRef> GetImportName(ImportExportKindCode kind,
uint32_t index) {
base::MutexGuard guard(&mutex_);
if (!import_names_) {
import_names_ = std::make_unique<
std::map<ImportExportKey, std::pair<WireBytesRef, WireBytesRef>>>();
for (auto imp : native_module_->module()->import_table) {
import_names_->insert(
std::make_pair(std::make_pair(imp.kind, imp.index),
std::make_pair(imp.module_name, imp.field_name)));
}
}
auto it = import_names_->find(std::make_pair(kind, index));
if (it != import_names_->end()) return it->second;
return {};
}
WireBytesRef GetTypeName(int type_index) {
base::MutexGuard guard(&mutex_);
if (!type_names_) {
type_names_ = std::make_unique<NameMap>(DecodeNameMap(
native_module_->wire_bytes(), NameSectionKindCode::kTypeCode));
}
return type_names_->GetName(type_index);
}
WireBytesRef GetLocalName(int func_index, int local_index) {
base::MutexGuard guard(&mutex_);
if (!local_names_) {
local_names_ = std::make_unique<IndirectNameMap>(DecodeIndirectNameMap(
native_module_->wire_bytes(), NameSectionKindCode::kLocalCode));
}
return local_names_->GetName(func_index, local_index);
}
WireBytesRef GetFieldName(int struct_index, int field_index) {
base::MutexGuard guard(&mutex_);
if (!field_names_) {
field_names_ = std::make_unique<IndirectNameMap>(DecodeIndirectNameMap(
native_module_->wire_bytes(), NameSectionKindCode::kFieldCode));
}
return field_names_->GetName(struct_index, field_index);
}
// If the frame position is not in the list of breakpoints, return that // If the frame position is not in the list of breakpoints, return that
// position. Return 0 otherwise. // position. Return 0 otherwise.
// This is used to generate a "dead breakpoint" in Liftoff, which is necessary // This is used to generate a "dead breakpoint" in Liftoff, which is necessary
...@@ -765,21 +705,6 @@ class DebugInfoImpl { ...@@ -765,21 +705,6 @@ class DebugInfoImpl {
}; };
std::vector<CachedDebuggingCode> cached_debugging_code_; std::vector<CachedDebuggingCode> cached_debugging_code_;
// Names of exports, lazily derived from the exports table.
std::unique_ptr<std::map<ImportExportKey, wasm::WireBytesRef>> export_names_;
// Names of imports, lazily derived from the imports table.
std::unique_ptr<std::map<ImportExportKey,
std::pair<wasm::WireBytesRef, wasm::WireBytesRef>>>
import_names_;
// Names of types, lazily decoded from the wire bytes.
std::unique_ptr<NameMap> type_names_;
// Names of locals, lazily decoded from the wire bytes.
std::unique_ptr<IndirectNameMap> local_names_;
// Names of struct fields, lazily decoded from the wire bytes.
std::unique_ptr<IndirectNameMap> field_names_;
// Isolate-specific data. // Isolate-specific data.
std::unordered_map<Isolate*, PerIsolateDebugData> per_isolate_data_; std::unordered_map<Isolate*, PerIsolateDebugData> per_isolate_data_;
}; };
...@@ -807,28 +732,6 @@ const wasm::WasmFunction& DebugInfo::GetFunctionAtAddress(Address pc) { ...@@ -807,28 +732,6 @@ const wasm::WasmFunction& DebugInfo::GetFunctionAtAddress(Address pc) {
return impl_->GetFunctionAtAddress(pc); return impl_->GetFunctionAtAddress(pc);
} }
WireBytesRef DebugInfo::GetExportName(ImportExportKindCode code,
uint32_t index) {
return impl_->GetExportName(code, index);
}
std::pair<WireBytesRef, WireBytesRef> DebugInfo::GetImportName(
ImportExportKindCode code, uint32_t index) {
return impl_->GetImportName(code, index);
}
WireBytesRef DebugInfo::GetTypeName(int type_index) {
return impl_->GetTypeName(type_index);
}
WireBytesRef DebugInfo::GetLocalName(int func_index, int local_index) {
return impl_->GetLocalName(func_index, local_index);
}
WireBytesRef DebugInfo::GetFieldName(int struct_index, int field_index) {
return impl_->GetFieldName(struct_index, field_index);
}
void DebugInfo::SetBreakpoint(int func_index, int offset, void DebugInfo::SetBreakpoint(int func_index, int offset,
Isolate* current_isolate) { Isolate* current_isolate) {
impl_->SetBreakpoint(func_index, offset, current_isolate); impl_->SetBreakpoint(func_index, offset, current_isolate);
......
...@@ -183,21 +183,6 @@ class V8_EXPORT_PRIVATE DebugInfo { ...@@ -183,21 +183,6 @@ class V8_EXPORT_PRIVATE DebugInfo {
WasmValue GetStackValue(int index, Address pc, Address fp, WasmValue GetStackValue(int index, Address pc, Address fp,
Address debug_break_fp, Isolate* isolate); Address debug_break_fp, Isolate* isolate);
// Returns the name of the entity (with the given |index| and |kind|) derived
// from the exports table. If the entity is not exported, an empty reference
// will be returned instead.
WireBytesRef GetExportName(ImportExportKindCode kind, uint32_t index);
// Returns the module and field name of the entity (with the given |index|
// and |kind|) derived from the imports table. If the entity is not imported,
// a pair of empty references will be returned instead.
std::pair<WireBytesRef, WireBytesRef> GetImportName(ImportExportKindCode kind,
uint32_t index);
WireBytesRef GetTypeName(int type_index);
WireBytesRef GetLocalName(int func_index, int local_index);
WireBytesRef GetFieldName(int struct_index, int field_index);
void SetBreakpoint(int func_index, int offset, Isolate* current_isolate); void SetBreakpoint(int func_index, int offset, Isolate* current_isolate);
// Returns true if we stay inside the passed frame (or a called frame) after // Returns true if we stay inside the passed frame (or a called frame) after
......
...@@ -8,17 +8,9 @@ ...@@ -8,17 +8,9 @@
#include <memory> #include <memory>
#include "src/api/api-inl.h" #include "src/api/api-inl.h"
#include "src/base/platform/wrappers.h"
#include "src/codegen/assembler-inl.h"
#include "src/compiler/wasm-compiler.h" #include "src/compiler/wasm-compiler.h"
#include "src/debug/interface-types.h"
#include "src/execution/frames-inl.h"
#include "src/execution/simulator.h"
#include "src/init/v8.h"
#include "src/objects/js-array-inl.h" #include "src/objects/js-array-inl.h"
#include "src/objects/objects.h" #include "src/objects/objects.h"
#include "src/objects/property-descriptor.h"
#include "src/snapshot/snapshot.h"
#include "src/wasm/module-decoder.h" #include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-init-expr.h" #include "src/wasm/wasm-init-expr.h"
...@@ -32,18 +24,23 @@ namespace internal { ...@@ -32,18 +24,23 @@ namespace internal {
namespace wasm { namespace wasm {
WireBytesRef LazilyGeneratedNames::LookupFunctionName( WireBytesRef LazilyGeneratedNames::LookupFunctionName(
const ModuleWireBytes& wire_bytes, uint32_t function_index) const { const ModuleWireBytes& wire_bytes, uint32_t function_index) {
base::MutexGuard lock(&mutex_); base::MutexGuard lock(&mutex_);
if (!function_names_) { if (!has_functions_) {
function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>()); has_functions_ = true;
DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(), DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(), function_names_);
function_names_.get());
} }
auto it = function_names_->find(function_index); auto it = function_names_.find(function_index);
if (it == function_names_->end()) return WireBytesRef(); if (it == function_names_.end()) return WireBytesRef();
return it->second; return it->second;
} }
bool LazilyGeneratedNames::Has(uint32_t function_index) {
DCHECK(has_functions_);
base::MutexGuard lock(&mutex_);
return function_names_.find(function_index) != function_names_.end();
}
// static // static
int MaxNumExportWrappers(const WasmModule* module) { int MaxNumExportWrappers(const WasmModule* module) {
// For each signature there may exist a wrapper, both for imported and // For each signature there may exist a wrapper, both for imported and
...@@ -130,10 +127,7 @@ int GetSubtypingDepth(const WasmModule* module, uint32_t type_index) { ...@@ -130,10 +127,7 @@ int GetSubtypingDepth(const WasmModule* module, uint32_t type_index) {
void LazilyGeneratedNames::AddForTesting(int function_index, void LazilyGeneratedNames::AddForTesting(int function_index,
WireBytesRef name) { WireBytesRef name) {
base::MutexGuard lock(&mutex_); base::MutexGuard lock(&mutex_);
if (!function_names_) { function_names_.insert(std::make_pair(function_index, name));
function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>());
}
function_names_->insert(std::make_pair(function_index, name));
} }
AsmJsOffsetInformation::AsmJsOffsetInformation( AsmJsOffsetInformation::AsmJsOffsetInformation(
......
...@@ -217,17 +217,17 @@ struct ModuleWireBytes; ...@@ -217,17 +217,17 @@ struct ModuleWireBytes;
class V8_EXPORT_PRIVATE LazilyGeneratedNames { class V8_EXPORT_PRIVATE LazilyGeneratedNames {
public: public:
WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes, WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes,
uint32_t function_index) const; uint32_t function_index);
void AddForTesting(int function_index, WireBytesRef name); void AddForTesting(int function_index, WireBytesRef name);
bool Has(uint32_t function_index);
private: private:
// {function_names_} are populated lazily after decoding, and // Lazy loading must guard against concurrent modifications from multiple
// therefore need a mutex to protect concurrent modifications // {WasmModuleObject}s.
// from multiple {WasmModuleObject}. base::Mutex mutex_;
mutable base::Mutex mutex_; bool has_functions_{false};
mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>> std::unordered_map<uint32_t, WireBytesRef> function_names_;
function_names_;
}; };
class V8_EXPORT_PRIVATE AsmJsOffsetInformation { class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
...@@ -422,6 +422,9 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -422,6 +422,9 @@ struct V8_EXPORT_PRIVATE WasmModule {
// ID and length). // ID and length).
WireBytesRef code = {0, 0}; WireBytesRef code = {0, 0};
WireBytesRef name = {0, 0}; WireBytesRef name = {0, 0};
// Position and size of the name section (payload only, i.e. without section
// ID and length).
WireBytesRef name_section = {0, 0};
void add_type(TypeDefinition type) { void add_type(TypeDefinition type) {
types.push_back(type); types.push_back(type);
...@@ -503,7 +506,7 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -503,7 +506,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
mutable TypeFeedbackStorage type_feedback; mutable TypeFeedbackStorage type_feedback;
ModuleOrigin origin = kWasmOrigin; // origin of the module ModuleOrigin origin = kWasmOrigin; // origin of the module
LazilyGeneratedNames lazily_generated_names; mutable LazilyGeneratedNames lazily_generated_names;
WasmDebugSymbols debug_symbols; WasmDebugSymbols debug_symbols;
// Asm.js source position information. Only available for modules compiled // Asm.js source position information. Only available for modules compiled
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment