v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
names-provider.cc
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
14
15namespace v8 {
16namespace internal {
17namespace wasm {
18
21 : module_(module), wire_bytes_(wire_bytes) {}
22
24
33
34// Function names are generally handled separately from other names; in
35// particular we support decoding function names without decoding any other
36// names, in which case also computing fallback names from imports and exports
37// must happen separately.
41 // When tracing streaming compilations, we might not yet have wire bytes.
42 if (wire_bytes_.empty()) return;
43 for (const WasmImport& import : module_->import_table) {
44 if (import.kind != kExternalFunction) continue;
45 if (module_->lazily_generated_names.Has(import.index)) continue;
47 }
48 for (const WasmExport& ex : module_->export_table) {
49 if (ex.kind != kExternalFunction) continue;
50 if (module_->lazily_generated_names.Has(ex.index)) continue;
52 }
53}
54
58 // When tracing streaming compilations, we might not yet have wire bytes.
59 if (wire_bytes_.empty()) return;
61 for (const WasmImport import : module_->import_table) {
62 switch (import.kind) {
64 continue; // Functions are handled separately.
65 case kExternalTable:
66 if (name_section_names_->table_names_.Has(import.index)) continue;
68 break;
69 case kExternalMemory:
70 if (name_section_names_->memory_names_.Has(import.index)) continue;
72 break;
73 case kExternalGlobal:
74 if (name_section_names_->global_names_.Has(import.index)) continue;
76 break;
77 case kExternalTag:
78 if (name_section_names_->tag_names_.Has(import.index)) continue;
80 break;
81 }
82 }
83 for (const WasmExport& ex : module_->export_table) {
84 switch (ex.kind) {
86 continue; // Functions are handled separately.
87 case kExternalTable:
88 if (name_section_names_->table_names_.Has(ex.index)) continue;
90 break;
91 case kExternalMemory:
92 if (name_section_names_->memory_names_.Has(ex.index)) continue;
94 break;
95 case kExternalGlobal:
96 if (name_section_names_->global_names_.Has(ex.index)) continue;
98 break;
99 case kExternalTag:
100 if (name_section_names_->tag_names_.Has(ex.index)) continue;
102 break;
103 }
104 }
105}
106
107namespace {
108// Any disallowed characters get replaced with '_'. Reference:
109// https://webassembly.github.io/spec/core/text/values.html#text-id
110static constexpr char kIdentifierChar[] = {
111 '_', '!', '_', '#', '$', '%', '&', '\'', // --
112 '_', '_', '*', '+', '_', '-', '.', '/', // --
113 '0', '1', '2', '3', '4', '5', '6', '7', // --
114 '8', '9', ':', '_', '<', '=', '>', '?', // --
115 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', // --
116 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // --
117 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', // --
118 'X', 'Y', 'Z', '_', '\\', '_', '^', '_', // --
119 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', // --
120 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // --
121 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', // --
122 'x', 'y', 'z', '_', '|', '_', '~', '_', // --
123};
124
125// To match legacy wasmparser behavior, we emit one '_' per invalid UTF16
126// code unit.
127// We could decide that we don't care much how exactly non-ASCII names are
128// rendered and simplify this to "one '_' per invalid UTF8 byte".
129void SanitizeUnicodeName(StringBuilder& out, const uint8_t* utf8_src,
130 size_t length) {
131 if (length == 0) return; // Illegal nullptrs arise below when length == 0.
132 base::Vector<const uint8_t> utf8_data(utf8_src, length);
133 Utf8Decoder decoder(utf8_data);
134 std::vector<uint16_t> utf16(decoder.utf16_length());
135 decoder.Decode(utf16.data(), utf8_data);
136 for (uint16_t c : utf16) {
137 if (c < 32 || c >= 127) {
138 out << '_';
139 } else {
140 out << kIdentifierChar[c - 32];
141 }
142 }
143}
144} // namespace
145
147 std::map<uint32_t, std::string>& target) {
148 const uint8_t* mod_start = wire_bytes_.begin() + import.module_name.offset();
149 size_t mod_length = import.module_name.length();
150 const uint8_t* field_start = wire_bytes_.begin() + import.field_name.offset();
151 size_t field_length = import.field_name.length();
152 StringBuilder buffer;
153 buffer << '$';
154 SanitizeUnicodeName(buffer, mod_start, mod_length);
155 buffer << '.';
156 SanitizeUnicodeName(buffer, field_start, field_length);
157 target[import.index] = std::string(buffer.start(), buffer.length());
158}
159
161 std::map<uint32_t, std::string>& target) {
162 if (target.find(ex.index) != target.end()) return;
163 size_t length = ex.name.length();
164 if (length == 0) return;
165 StringBuilder buffer;
166 buffer << '$';
167 SanitizeUnicodeName(buffer, wire_bytes_.begin() + ex.name.offset(), length);
168 target[ex.index] = std::string(buffer.start(), buffer.length());
169}
170
171namespace {
172
173V8_INLINE void MaybeAddComment(StringBuilder& out, uint32_t index,
174 bool add_comment) {
175 if (add_comment) out << " (;" << index << ";)";
176}
177
178} // namespace
179
181 out.write(wire_bytes_.begin() + ref.offset(), ref.length());
182}
183
185 uint32_t function_index,
186 FunctionNamesBehavior behavior,
187 IndexAsComment index_as_comment) {
188 // Function names are stored elsewhere, because we need to access them
189 // during (streaming) compilation when the NamesProvider isn't ready yet.
190 WireBytesRef ref = module_->lazily_generated_names.LookupFunctionName(
191 ModuleWireBytes(wire_bytes_), function_index);
192 if (ref.is_set()) {
193 if (behavior == kDevTools) {
194 out << '$';
195 WriteRef(out, ref);
196 MaybeAddComment(out, function_index, index_as_comment);
197 } else {
198 // For kWasmInternal behavior, function names don't get a `$` prefix.
199 WriteRef(out, ref);
200 }
201 return;
202 }
203
204 if (behavior == kWasmInternal) return;
205 {
209 }
210 }
211 auto it = import_export_function_names_.find(function_index);
212 if (it != import_export_function_names_.end()) {
213 out << it->second;
214 MaybeAddComment(out, function_index, index_as_comment);
215 } else {
216 out << "$func" << function_index;
217 }
218}
219
220WireBytesRef Get(const NameMap& map, uint32_t index) {
221 const WireBytesRef* result = map.Get(index);
222 if (!result) return {};
223 return *result;
224}
225
226WireBytesRef Get(const IndirectNameMap& map, uint32_t outer_index,
227 uint32_t inner_index) {
228 const NameMap* inner = map.Get(outer_index);
229 if (!inner) return {};
230 return Get(*inner, inner_index);
231}
232
233void NamesProvider::PrintLocalName(StringBuilder& out, uint32_t function_index,
234 uint32_t local_index,
235 IndexAsComment index_as_comment) {
237 WireBytesRef ref =
238 Get(name_section_names_->local_names_, function_index, local_index);
239 if (ref.is_set()) {
240 out << '$';
241 WriteRef(out, ref);
242 MaybeAddComment(out, local_index, index_as_comment);
243 } else {
244 out << "$var" << local_index;
245 }
246}
247
248void NamesProvider::PrintLabelName(StringBuilder& out, uint32_t function_index,
249 uint32_t label_index,
250 uint32_t fallback_index) {
252 WireBytesRef ref =
253 Get(name_section_names_->label_names_, function_index, label_index);
254 if (ref.is_set()) {
255 out << '$';
256 WriteRef(out, ref);
257 } else {
258 out << "$label" << fallback_index;
259 }
260}
261
262void NamesProvider::PrintTypeName(StringBuilder& out, uint32_t type_index,
263 IndexAsComment index_as_comment) {
265 WireBytesRef ref = Get(name_section_names_->type_names_, type_index);
266 if (ref.is_set()) {
267 out << '$';
268 WriteRef(out, ref);
269 return MaybeAddComment(out, type_index, index_as_comment);
270 }
271 out << "$type" << type_index;
272}
273
274void NamesProvider::PrintTableName(StringBuilder& out, uint32_t table_index,
275 IndexAsComment index_as_comment) {
277 WireBytesRef ref = Get(name_section_names_->table_names_, table_index);
278 if (ref.is_set()) {
279 out << '$';
280 WriteRef(out, ref);
281 return MaybeAddComment(out, table_index, index_as_comment);
282 }
283
284 auto it = import_export_table_names_.find(table_index);
285 if (it != import_export_table_names_.end()) {
286 out << it->second;
287 return MaybeAddComment(out, table_index, index_as_comment);
288 }
289 out << "$table" << table_index;
290}
291
292void NamesProvider::PrintMemoryName(StringBuilder& out, uint32_t memory_index,
293 IndexAsComment index_as_comment) {
295 WireBytesRef ref = Get(name_section_names_->memory_names_, memory_index);
296 if (ref.is_set()) {
297 out << '$';
298 WriteRef(out, ref);
299 return MaybeAddComment(out, memory_index, index_as_comment);
300 }
301
302 auto it = import_export_memory_names_.find(memory_index);
303 if (it != import_export_memory_names_.end()) {
304 out << it->second;
305 return MaybeAddComment(out, memory_index, index_as_comment);
306 }
307
308 out << "$memory" << memory_index;
309}
310
311void NamesProvider::PrintGlobalName(StringBuilder& out, uint32_t global_index,
312 IndexAsComment index_as_comment) {
314 WireBytesRef ref = Get(name_section_names_->global_names_, global_index);
315 if (ref.is_set()) {
316 out << '$';
317 WriteRef(out, ref);
318 return MaybeAddComment(out, global_index, index_as_comment);
319 }
320
321 auto it = import_export_global_names_.find(global_index);
322 if (it != import_export_global_names_.end()) {
323 out << it->second;
324 return MaybeAddComment(out, global_index, index_as_comment);
325 }
326
327 out << "$global" << global_index;
328}
329
331 uint32_t element_segment_index,
332 IndexAsComment index_as_comment) {
334 WireBytesRef ref =
335 Get(name_section_names_->element_segment_names_, element_segment_index);
336 if (ref.is_set()) {
337 out << '$';
338 WriteRef(out, ref);
339 MaybeAddComment(out, element_segment_index, index_as_comment);
340 } else {
341 out << "$elem" << element_segment_index;
342 }
343}
344
346 uint32_t data_segment_index,
347 IndexAsComment index_as_comment) {
349 WireBytesRef ref =
350 Get(name_section_names_->data_segment_names_, data_segment_index);
351 if (ref.is_set()) {
352 out << '$';
353 WriteRef(out, ref);
354 MaybeAddComment(out, data_segment_index, index_as_comment);
355 } else {
356 out << "$data" << data_segment_index;
357 }
358}
359
360void NamesProvider::PrintFieldName(StringBuilder& out, uint32_t struct_index,
361 uint32_t field_index,
362 IndexAsComment index_as_comment) {
364 WireBytesRef ref =
365 Get(name_section_names_->field_names_, struct_index, field_index);
366 if (ref.is_set()) {
367 out << '$';
368 WriteRef(out, ref);
369 return MaybeAddComment(out, field_index, index_as_comment);
370 }
371 out << "$field" << field_index;
372}
373
375 IndexAsComment index_as_comment) {
377 WireBytesRef ref = Get(name_section_names_->tag_names_, tag_index);
378 if (ref.is_set()) {
379 out << '$';
380 WriteRef(out, ref);
381 return MaybeAddComment(out, tag_index, index_as_comment);
382 }
383 auto it = import_export_tag_names_.find(tag_index);
384 if (it != import_export_tag_names_.end()) {
385 out << it->second;
386 return MaybeAddComment(out, tag_index, index_as_comment);
387 }
388 out << "$tag" << tag_index;
389}
390
392 if (type.is_index()) {
393 if (type.is_exact()) out << "exact ";
394 PrintTypeName(out, type.ref_index());
395 } else {
396 out << type.name();
397 }
398}
399
401 if (type.has_index()) {
402 out << (type.is_nullable() ? "(ref null " : "(ref ");
403 if (type.is_exact()) out << "exact ";
404 PrintTypeName(out, type.ref_index());
405 out << ')';
406 } else {
407 out << type.name();
408 }
409}
410
411namespace {
412size_t StringMapSize(const std::map<uint32_t, std::string>& map) {
413 size_t result = ContentSize(map);
414 for (const auto& entry : map) {
415 result += entry.second.size();
416 }
417 return result;
418}
419} // namespace
420
423 size_t result = sizeof(NamesProvider);
427 result += names->label_names_.EstimateCurrentMemoryConsumption();
428 result += names->type_names_.EstimateCurrentMemoryConsumption();
429 result += names->table_names_.EstimateCurrentMemoryConsumption();
430 result += names->memory_names_.EstimateCurrentMemoryConsumption();
431 result += names->global_names_.EstimateCurrentMemoryConsumption();
432 result += names->element_segment_names_.EstimateCurrentMemoryConsumption();
433 result += names->data_segment_names_.EstimateCurrentMemoryConsumption();
434 result += names->field_names_.EstimateCurrentMemoryConsumption();
435 result += names->tag_names_.EstimateCurrentMemoryConsumption();
436 }
437 {
439 result += StringMapSize(import_export_function_names_);
440 result += StringMapSize(import_export_table_names_);
441 result += StringMapSize(import_export_memory_names_);
442 result += StringMapSize(import_export_global_names_);
443 result += StringMapSize(import_export_tag_names_);
444 }
445 if (v8_flags.trace_wasm_offheap_memory) {
446 PrintF("NamesProvider: %zu\n", result);
447 }
448 return result;
449}
450
452 size_t result = sizeof(this) + payload_size_estimate_;
453 result += type_names_.capacity() * sizeof(StringT);
455 for (const auto& entry : field_names_) {
456 const std::vector<StringT>& vec = entry.second;
457 result += vec.capacity() * sizeof(StringT);
458 }
459 if (v8_flags.trace_wasm_offheap_memory) {
460 PrintF("CanonicalTypeNamesProvider: %zu\n", result);
461 }
462 return result;
463}
464
466 // TODO(jkummerow): We'll probably need to lock read accesses too.
468 type_names_.resize(GetTypeCanonicalizer()->GetCurrentNumberOfTypes());
470}
471
473 const WasmModule* module = native_module->module();
474 if (module->canonical_typenames_decoded) return;
475 module->canonical_typenames_decoded = true;
476 base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
477 WireBytesRef name_section = module->name_section;
478 if (name_section.is_empty()) return;
479 size_t added_size = 0;
481 &added_size);
482 payload_size_estimate_ += added_size;
483}
484
486 StringBuilder& out, CanonicalTypeIndex type_index,
487 NamesProvider::IndexAsComment index_as_comment) {
488 uint32_t index = type_index.index;
489 if (index > type_names_.size() || type_names_[index].empty()) {
491 }
492 // {index} should now always be in range, but let's be robust towards
493 // invalid parameter values.
494 if (index > type_names_.size() || type_names_[index].empty()) {
495 out << "$canon" << index;
496 return;
497 }
498 StringT& name = type_names_[index];
499 out << '$';
500 out.write(name.data(), name.size());
501 MaybeAddComment(out, index, index_as_comment);
502}
503
505 CanonicalValueType type) {
506 switch (type.kind()) {
507 case kRef:
508 case kRefNull:
509 if (type.encoding_needs_heap_type()) {
510 out << (type.kind() == kRef ? "(ref " : "(ref null ");
511 if (type.is_exact()) out << "exact ";
512 if (type.has_index()) {
513 PrintTypeName(out, type.ref_index());
514 } else {
515 out << type.name();
516 }
517 out << ')';
518 } else {
519 out << type.name();
520 }
521 break;
522 default:
523 out << wasm::name(type.kind());
524 }
525}
526
528 CanonicalTypeIndex struct_index,
529 uint32_t field_index) {
530 uint32_t index = struct_index.index;
531 if (index > type_names_.size()) DecodeNameSections();
532
533 auto per_type = field_names_.find(index);
534 if (per_type != field_names_.end()) {
535 std::vector<StringT>& field_names = per_type->second;
536 if (field_index < field_names.size() && !field_names[field_index].empty()) {
537 const StringT& name = field_names[field_index];
538 out << '$';
539 out.write(name.data(), name.size());
540 return;
541 }
542 }
543 out << "$field" << field_index;
544}
545
546// At the time of this writing, different std::string implementations
547// support 15 to 23 characters for inline storage. For accurate tracking
548// of memory consumption, dynamically determine this threshold.
550 for (size_t i = 0; i < 32; i++) {
551 std::string s(i, 'c');
552 Address str = reinterpret_cast<Address>(&s);
553 Address data = reinterpret_cast<Address>(s.data());
554 if (data < str || data >= str + sizeof(s)) return i;
555 }
556 return 32;
557}
558
559} // namespace wasm
560} // namespace internal
561} // namespace v8
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
Builtins::Kind kind
Definition builtins.cc:40
constexpr bool empty() const
Definition vector.h:73
constexpr T * begin() const
Definition vector.h:96
size_t EstimateCurrentMemoryConsumption() const
const Value * Get(uint32_t key) const
void PrintValueType(StringBuilder &out, CanonicalValueType type)
void PrintFieldName(StringBuilder &out, CanonicalTypeIndex struct_index, uint32_t field_index)
void PrintTypeName(StringBuilder &out, CanonicalTypeIndex type_index, NamesProvider::IndexAsComment index_as_comment=NamesProvider::kDontPrintIndex)
std::map< uint32_t, std::vector< StringT > > field_names_
void DecodeNames(NativeModule *native_module)
std::map< uint32_t, std::string > import_export_function_names_
std::map< uint32_t, std::string > import_export_memory_names_
void PrintLabelName(StringBuilder &out, uint32_t function_index, uint32_t label_index, uint32_t fallback_index)
base::Vector< const uint8_t > wire_bytes_
void PrintGlobalName(StringBuilder &out, uint32_t global_index, IndexAsComment index_as_comment=kDontPrintIndex)
void PrintTagName(StringBuilder &out, uint32_t tag_index, IndexAsComment index_as_comment=kDontPrintIndex)
void PrintDataSegmentName(StringBuilder &out, uint32_t data_segment_index, IndexAsComment index_as_comment=kDontPrintIndex)
void PrintLocalName(StringBuilder &out, uint32_t function_index, uint32_t local_index, IndexAsComment index_as_comment=kDontPrintIndex)
void PrintValueType(StringBuilder &out, ValueType type)
void PrintTypeName(StringBuilder &out, uint32_t type_index, IndexAsComment index_as_comment=kDontPrintIndex)
std::unique_ptr< DecodedNameSection > name_section_names_
void PrintFunctionName(StringBuilder &out, uint32_t function_index, FunctionNamesBehavior behavior=kWasmInternal, IndexAsComment index_as_comment=kDontPrintIndex)
NamesProvider(const WasmModule *module, base::Vector< const uint8_t > wire_bytes)
void PrintHeapType(StringBuilder &out, HeapType type)
void PrintFieldName(StringBuilder &out, uint32_t struct_index, uint32_t field_index, IndexAsComment index_as_comment=kDontPrintIndex)
void PrintMemoryName(StringBuilder &out, uint32_t memory_index, IndexAsComment index_as_comment=kDontPrintIndex)
void ComputeExportName(const WasmExport &ex, std::map< uint32_t, std::string > &target)
void PrintElementSegmentName(StringBuilder &out, uint32_t element_segment_index, IndexAsComment index_as_comment=kDontPrintIndex)
void PrintTableName(StringBuilder &out, uint32_t table_index, IndexAsComment index_as_comment=kDontPrintIndex)
void ComputeImportName(const WasmImport &import, std::map< uint32_t, std::string > &target)
std::map< uint32_t, std::string > import_export_table_names_
void WriteRef(StringBuilder &out, WireBytesRef ref)
std::map< uint32_t, std::string > import_export_global_names_
std::map< uint32_t, std::string > import_export_tag_names_
const WasmModule * module() const
base::Vector< const uint8_t > wire_bytes() const
void DecodeAllNameSections(CanonicalTypeNamesProvider *target)
ZoneVector< RpoNumber > & result
const base::Vector< const uint8_t > wire_bytes_
int s
Definition mul-fft.cc:297
void DecodeCanonicalTypeNames(base::Vector< const uint8_t > wire_bytes, const WasmModule *module, std::vector< base::OwnedVector< char > > &typenames, std::map< uint32_t, std::vector< base::OwnedVector< char > > > &fieldnames, size_t *total_allocated_size)
TypeCanonicalizer * GetTypeCanonicalizer()
constexpr const char * name(ValueKind kind)
size_t ContentSize(const std::vector< T > &vector)
WireBytesRef Get(const NameMap &map, uint32_t index)
WasmEngine * GetWasmEngine()
void PrintF(const char *format,...)
Definition utils.cc:39
V8_EXPORT_PRIVATE FlagValues v8_flags
Definition c-api.cc:87
#define DCHECK(condition)
Definition logging.h:482
#define UPDATE_WHEN_CLASS_CHANGES(classname, size)
ImportExportKindCode kind
std::atomic< bool > canonical_typenames_decoded
#define V8_INLINE
Definition v8config.h:500
const wasm::WasmModule * module_