xref: /third_party/node/deps/v8/src/wasm/wasm-module.cc (revision 1cb0ef41)
1// Copyright 2015 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
5#include "src/wasm/wasm-module.h"
6
7#include <functional>
8#include <memory>
9
10#include "src/api/api-inl.h"
11#include "src/base/platform/wrappers.h"
12#include "src/codegen/assembler-inl.h"
13#include "src/compiler/wasm-compiler.h"
14#include "src/debug/interface-types.h"
15#include "src/execution/frames-inl.h"
16#include "src/execution/simulator.h"
17#include "src/init/v8.h"
18#include "src/objects/js-array-inl.h"
19#include "src/objects/objects.h"
20#include "src/objects/property-descriptor.h"
21#include "src/snapshot/snapshot.h"
22#include "src/wasm/module-decoder.h"
23#include "src/wasm/wasm-code-manager.h"
24#include "src/wasm/wasm-init-expr.h"
25#include "src/wasm/wasm-js.h"
26#include "src/wasm/wasm-objects-inl.h"
27#include "src/wasm/wasm-result.h"
28#include "src/wasm/wasm-subtyping.h"
29
30namespace v8 {
31namespace internal {
32namespace wasm {
33
34WireBytesRef LazilyGeneratedNames::LookupFunctionName(
35    const ModuleWireBytes& wire_bytes, uint32_t function_index) const {
36  base::MutexGuard lock(&mutex_);
37  if (!function_names_) {
38    function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>());
39    DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(),
40                        function_names_.get());
41  }
42  auto it = function_names_->find(function_index);
43  if (it == function_names_->end()) return WireBytesRef();
44  return it->second;
45}
46
47// static
48int MaxNumExportWrappers(const WasmModule* module) {
49  // For each signature there may exist a wrapper, both for imported and
50  // internal functions.
51  return static_cast<int>(module->signature_map.size()) * 2;
52}
53
54int GetExportWrapperIndexInternal(const WasmModule* module,
55                                  int canonical_sig_index, bool is_import) {
56  if (is_import) canonical_sig_index += module->signature_map.size();
57  return canonical_sig_index;
58}
59
60int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
61                          bool is_import) {
62  int canonical_sig_index = module->signature_map.Find(*sig);
63  CHECK_GE(canonical_sig_index, 0);
64  return GetExportWrapperIndexInternal(module, canonical_sig_index, is_import);
65}
66
67int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
68                          bool is_import) {
69  uint32_t canonical_sig_index = module->canonicalized_type_ids[sig_index];
70  return GetExportWrapperIndexInternal(module, canonical_sig_index, is_import);
71}
72
73// static
74int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index) {
75  const std::vector<WasmFunction>& functions = module->functions;
76  if (static_cast<uint32_t>(func_index) >= functions.size()) return -1;
77  DCHECK_GE(kMaxInt, functions[func_index].code.offset());
78  return static_cast<int>(functions[func_index].code.offset());
79}
80
81// static
82int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset) {
83  const std::vector<WasmFunction>& functions = module->functions;
84
85  // Binary search for a function containing the given position.
86  int left = 0;                                    // inclusive
87  int right = static_cast<int>(functions.size());  // exclusive
88  if (right == 0) return -1;
89  while (right - left > 1) {
90    int mid = left + (right - left) / 2;
91    if (functions[mid].code.offset() <= byte_offset) {
92      left = mid;
93    } else {
94      right = mid;
95    }
96  }
97
98  return left;
99}
100
101// static
102int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
103  int func_index = GetNearestWasmFunction(module, byte_offset);
104
105  if (func_index >= 0) {
106    // If the found function does not contain the given position, return -1.
107    const WasmFunction& func = module->functions[func_index];
108    if (byte_offset < func.code.offset() ||
109        byte_offset >= func.code.end_offset()) {
110      return -1;
111    }
112  }
113  return func_index;
114}
115
116// TODO(7748): Measure whether this iterative implementation is fast enough.
117// We could cache the result on the module, in yet another vector indexed by
118// type index.
119int GetSubtypingDepth(const WasmModule* module, uint32_t type_index) {
120  uint32_t starting_point = type_index;
121  int depth = 0;
122  while ((type_index = module->supertype(type_index)) != kNoSuperType) {
123    if (type_index == starting_point) return -1;  // Cycle detected.
124    depth++;
125    if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) break;
126  }
127  return depth;
128}
129
130void LazilyGeneratedNames::AddForTesting(int function_index,
131                                         WireBytesRef name) {
132  base::MutexGuard lock(&mutex_);
133  if (!function_names_) {
134    function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>());
135  }
136  function_names_->insert(std::make_pair(function_index, name));
137}
138
139AsmJsOffsetInformation::AsmJsOffsetInformation(
140    base::Vector<const byte> encoded_offsets)
141    : encoded_offsets_(base::OwnedVector<const uint8_t>::Of(encoded_offsets)) {}
142
143AsmJsOffsetInformation::~AsmJsOffsetInformation() = default;
144
145int AsmJsOffsetInformation::GetSourcePosition(int declared_func_index,
146                                              int byte_offset,
147                                              bool is_at_number_conversion) {
148  EnsureDecodedOffsets();
149
150  DCHECK_LE(0, declared_func_index);
151  DCHECK_GT(decoded_offsets_->functions.size(), declared_func_index);
152  std::vector<AsmJsOffsetEntry>& function_offsets =
153      decoded_offsets_->functions[declared_func_index].entries;
154
155  auto byte_offset_less = [](const AsmJsOffsetEntry& a,
156                             const AsmJsOffsetEntry& b) {
157    return a.byte_offset < b.byte_offset;
158  };
159  SLOW_DCHECK(std::is_sorted(function_offsets.begin(), function_offsets.end(),
160                             byte_offset_less));
161  auto it =
162      std::lower_bound(function_offsets.begin(), function_offsets.end(),
163                       AsmJsOffsetEntry{byte_offset, 0, 0}, byte_offset_less);
164  DCHECK_NE(function_offsets.end(), it);
165  DCHECK_EQ(byte_offset, it->byte_offset);
166  return is_at_number_conversion ? it->source_position_number_conversion
167                                 : it->source_position_call;
168}
169
170std::pair<int, int> AsmJsOffsetInformation::GetFunctionOffsets(
171    int declared_func_index) {
172  EnsureDecodedOffsets();
173
174  DCHECK_LE(0, declared_func_index);
175  DCHECK_GT(decoded_offsets_->functions.size(), declared_func_index);
176  AsmJsOffsetFunctionEntries& function_info =
177      decoded_offsets_->functions[declared_func_index];
178
179  return {function_info.start_offset, function_info.end_offset};
180}
181
182void AsmJsOffsetInformation::EnsureDecodedOffsets() {
183  base::MutexGuard mutex_guard(&mutex_);
184  DCHECK_EQ(encoded_offsets_ == nullptr, decoded_offsets_ != nullptr);
185
186  if (decoded_offsets_) return;
187  AsmJsOffsetsResult result =
188      wasm::DecodeAsmJsOffsets(encoded_offsets_.as_vector());
189  decoded_offsets_ = std::make_unique<AsmJsOffsets>(std::move(result).value());
190  encoded_offsets_.ReleaseData();
191}
192
193// Get a string stored in the module bytes representing a name.
194WasmName ModuleWireBytes::GetNameOrNull(WireBytesRef ref) const {
195  if (!ref.is_set()) return {nullptr, 0};  // no name.
196  DCHECK(BoundsCheck(ref));
197  return WasmName::cast(
198      module_bytes_.SubVector(ref.offset(), ref.end_offset()));
199}
200
201// Get a string stored in the module bytes representing a function name.
202WasmName ModuleWireBytes::GetNameOrNull(const WasmFunction* function,
203                                        const WasmModule* module) const {
204  return GetNameOrNull(module->lazily_generated_names.LookupFunctionName(
205      *this, function->func_index));
206}
207
208std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) {
209  os << "#" << name.function_->func_index;
210  if (!name.name_.empty()) {
211    if (name.name_.begin()) {
212      os << ":";
213      os.write(name.name_.begin(), name.name_.length());
214    }
215  } else {
216    os << "?";
217  }
218  return os;
219}
220
221WasmModule::WasmModule(std::unique_ptr<Zone> signature_zone)
222    : signature_zone(std::move(signature_zone)) {}
223
224bool IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) {
225  // TODO(wasm): Once wasm has its own CSP policy, we should introduce a
226  // separate callback that includes information about the module about to be
227  // compiled. For the time being, pass an empty string as placeholder for the
228  // sources.
229  if (auto wasm_codegen_callback = isolate->allow_wasm_code_gen_callback()) {
230    return wasm_codegen_callback(
231        v8::Utils::ToLocal(context),
232        v8::Utils::ToLocal(isolate->factory()->empty_string()));
233  }
234  auto codegen_callback = isolate->allow_code_gen_callback();
235  return codegen_callback == nullptr ||
236         codegen_callback(
237             v8::Utils::ToLocal(context),
238             v8::Utils::ToLocal(isolate->factory()->empty_string()));
239}
240
241namespace {
242
243// Converts the given {type} into a string representation that can be used in
244// reflective functions. Should be kept in sync with the {GetValueType} helper.
245Handle<String> ToValueTypeString(Isolate* isolate, ValueType type) {
246  return isolate->factory()->InternalizeUtf8String(base::VectorOf(type.name()));
247}
248}  // namespace
249
250Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig,
251                                    bool for_exception) {
252  Factory* factory = isolate->factory();
253
254  // Extract values for the {ValueType[]} arrays.
255  int param_index = 0;
256  int param_count = static_cast<int>(sig->parameter_count());
257  Handle<FixedArray> param_values = factory->NewFixedArray(param_count);
258  for (ValueType type : sig->parameters()) {
259    Handle<String> type_value = ToValueTypeString(isolate, type);
260    param_values->set(param_index++, *type_value);
261  }
262
263  // Create the resulting {FunctionType} object.
264  Handle<JSFunction> object_function = isolate->object_function();
265  Handle<JSObject> object = factory->NewJSObject(object_function);
266  Handle<JSArray> params = factory->NewJSArrayWithElements(param_values);
267  Handle<String> params_string = factory->InternalizeUtf8String("parameters");
268  Handle<String> results_string = factory->InternalizeUtf8String("results");
269  JSObject::AddProperty(isolate, object, params_string, params, NONE);
270
271  // Now add the result types if needed.
272  if (for_exception) {
273    DCHECK_EQ(sig->returns().size(), 0);
274  } else {
275    int result_index = 0;
276    int result_count = static_cast<int>(sig->return_count());
277    Handle<FixedArray> result_values = factory->NewFixedArray(result_count);
278    for (ValueType type : sig->returns()) {
279      Handle<String> type_value = ToValueTypeString(isolate, type);
280      result_values->set(result_index++, *type_value);
281    }
282    Handle<JSArray> results = factory->NewJSArrayWithElements(result_values);
283    JSObject::AddProperty(isolate, object, results_string, results, NONE);
284  }
285
286  return object;
287}
288
289Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
290                                  ValueType type) {
291  Factory* factory = isolate->factory();
292
293  Handle<JSFunction> object_function = isolate->object_function();
294  Handle<JSObject> object = factory->NewJSObject(object_function);
295  Handle<String> mutable_string = factory->InternalizeUtf8String("mutable");
296  Handle<String> value_string = factory->InternalizeUtf8String("value");
297  JSObject::AddProperty(isolate, object, mutable_string,
298                        factory->ToBoolean(is_mutable), NONE);
299  JSObject::AddProperty(isolate, object, value_string,
300                        ToValueTypeString(isolate, type), NONE);
301
302  return object;
303}
304
305Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
306                                  base::Optional<uint32_t> max_size,
307                                  bool shared) {
308  Factory* factory = isolate->factory();
309
310  Handle<JSFunction> object_function = isolate->object_function();
311  Handle<JSObject> object = factory->NewJSObject(object_function);
312  Handle<String> minimum_string = factory->InternalizeUtf8String("minimum");
313  Handle<String> maximum_string = factory->InternalizeUtf8String("maximum");
314  Handle<String> shared_string = factory->InternalizeUtf8String("shared");
315  JSObject::AddProperty(isolate, object, minimum_string,
316                        factory->NewNumberFromUint(min_size), NONE);
317  if (max_size.has_value()) {
318    JSObject::AddProperty(isolate, object, maximum_string,
319                          factory->NewNumberFromUint(max_size.value()), NONE);
320  }
321  JSObject::AddProperty(isolate, object, shared_string,
322                        factory->ToBoolean(shared), NONE);
323
324  return object;
325}
326
327Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
328                                 uint32_t min_size,
329                                 base::Optional<uint32_t> max_size) {
330  Factory* factory = isolate->factory();
331
332  Handle<String> element =
333      factory->InternalizeUtf8String(base::VectorOf(type.name()));
334
335  Handle<JSFunction> object_function = isolate->object_function();
336  Handle<JSObject> object = factory->NewJSObject(object_function);
337  Handle<String> element_string = factory->InternalizeUtf8String("element");
338  Handle<String> minimum_string = factory->InternalizeUtf8String("minimum");
339  Handle<String> maximum_string = factory->InternalizeUtf8String("maximum");
340  JSObject::AddProperty(isolate, object, element_string, element, NONE);
341  JSObject::AddProperty(isolate, object, minimum_string,
342                        factory->NewNumberFromUint(min_size), NONE);
343  if (max_size.has_value()) {
344    JSObject::AddProperty(isolate, object, maximum_string,
345                          factory->NewNumberFromUint(max_size.value()), NONE);
346  }
347
348  return object;
349}
350
351Handle<JSArray> GetImports(Isolate* isolate,
352                           Handle<WasmModuleObject> module_object) {
353  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(isolate);
354  Factory* factory = isolate->factory();
355
356  Handle<String> module_string = factory->InternalizeUtf8String("module");
357  Handle<String> name_string = factory->InternalizeUtf8String("name");
358  Handle<String> kind_string = factory->InternalizeUtf8String("kind");
359  Handle<String> type_string = factory->InternalizeUtf8String("type");
360
361  Handle<String> function_string = factory->InternalizeUtf8String("function");
362  Handle<String> table_string = factory->InternalizeUtf8String("table");
363  Handle<String> memory_string = factory->InternalizeUtf8String("memory");
364  Handle<String> global_string = factory->InternalizeUtf8String("global");
365  Handle<String> tag_string = factory->InternalizeUtf8String("tag");
366
367  // Create the result array.
368  const WasmModule* module = module_object->module();
369  int num_imports = static_cast<int>(module->import_table.size());
370  Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
371  Handle<FixedArray> storage = factory->NewFixedArray(num_imports);
372  JSArray::SetContent(array_object, storage);
373  array_object->set_length(Smi::FromInt(num_imports));
374
375  Handle<JSFunction> object_function =
376      Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
377
378  // Populate the result array.
379  for (int index = 0; index < num_imports; ++index) {
380    const WasmImport& import = module->import_table[index];
381
382    Handle<JSObject> entry = factory->NewJSObject(object_function);
383
384    Handle<String> import_kind;
385    Handle<JSObject> type_value;
386    switch (import.kind) {
387      case kExternalFunction:
388        if (enabled_features.has_type_reflection()) {
389          auto& func = module->functions[import.index];
390          type_value = GetTypeForFunction(isolate, func.sig);
391        }
392        import_kind = function_string;
393        break;
394      case kExternalTable:
395        if (enabled_features.has_type_reflection()) {
396          auto& table = module->tables[import.index];
397          base::Optional<uint32_t> maximum_size;
398          if (table.has_maximum_size) maximum_size.emplace(table.maximum_size);
399          type_value = GetTypeForTable(isolate, table.type, table.initial_size,
400                                       maximum_size);
401        }
402        import_kind = table_string;
403        break;
404      case kExternalMemory:
405        if (enabled_features.has_type_reflection()) {
406          DCHECK_EQ(0, import.index);  // Only one memory supported.
407          base::Optional<uint32_t> maximum_size;
408          if (module->has_maximum_pages) {
409            maximum_size.emplace(module->maximum_pages);
410          }
411          type_value =
412              GetTypeForMemory(isolate, module->initial_pages, maximum_size,
413                               module->has_shared_memory);
414        }
415        import_kind = memory_string;
416        break;
417      case kExternalGlobal:
418        if (enabled_features.has_type_reflection()) {
419          auto& global = module->globals[import.index];
420          type_value =
421              GetTypeForGlobal(isolate, global.mutability, global.type);
422        }
423        import_kind = global_string;
424        break;
425      case kExternalTag:
426        import_kind = tag_string;
427        break;
428    }
429    DCHECK(!import_kind->is_null());
430
431    Handle<String> import_module =
432        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
433            isolate, module_object, import.module_name, kInternalize);
434
435    Handle<String> import_name =
436        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
437            isolate, module_object, import.field_name, kInternalize);
438
439    JSObject::AddProperty(isolate, entry, module_string, import_module, NONE);
440    JSObject::AddProperty(isolate, entry, name_string, import_name, NONE);
441    JSObject::AddProperty(isolate, entry, kind_string, import_kind, NONE);
442    if (!type_value.is_null()) {
443      JSObject::AddProperty(isolate, entry, type_string, type_value, NONE);
444    }
445
446    storage->set(index, *entry);
447  }
448
449  return array_object;
450}
451
452Handle<JSArray> GetExports(Isolate* isolate,
453                           Handle<WasmModuleObject> module_object) {
454  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(isolate);
455  Factory* factory = isolate->factory();
456
457  Handle<String> name_string = factory->InternalizeUtf8String("name");
458  Handle<String> kind_string = factory->InternalizeUtf8String("kind");
459  Handle<String> type_string = factory->InternalizeUtf8String("type");
460
461  Handle<String> function_string = factory->InternalizeUtf8String("function");
462  Handle<String> table_string = factory->InternalizeUtf8String("table");
463  Handle<String> memory_string = factory->InternalizeUtf8String("memory");
464  Handle<String> global_string = factory->InternalizeUtf8String("global");
465  Handle<String> tag_string = factory->InternalizeUtf8String("tag");
466
467  // Create the result array.
468  const WasmModule* module = module_object->module();
469  int num_exports = static_cast<int>(module->export_table.size());
470  Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
471  Handle<FixedArray> storage = factory->NewFixedArray(num_exports);
472  JSArray::SetContent(array_object, storage);
473  array_object->set_length(Smi::FromInt(num_exports));
474
475  Handle<JSFunction> object_function =
476      Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
477
478  // Populate the result array.
479  for (int index = 0; index < num_exports; ++index) {
480    const WasmExport& exp = module->export_table[index];
481
482    Handle<String> export_kind;
483    Handle<JSObject> type_value;
484    switch (exp.kind) {
485      case kExternalFunction:
486        if (enabled_features.has_type_reflection()) {
487          auto& func = module->functions[exp.index];
488          type_value = GetTypeForFunction(isolate, func.sig);
489        }
490        export_kind = function_string;
491        break;
492      case kExternalTable:
493        if (enabled_features.has_type_reflection()) {
494          auto& table = module->tables[exp.index];
495          base::Optional<uint32_t> maximum_size;
496          if (table.has_maximum_size) maximum_size.emplace(table.maximum_size);
497          type_value = GetTypeForTable(isolate, table.type, table.initial_size,
498                                       maximum_size);
499        }
500        export_kind = table_string;
501        break;
502      case kExternalMemory:
503        if (enabled_features.has_type_reflection()) {
504          DCHECK_EQ(0, exp.index);  // Only one memory supported.
505          base::Optional<uint32_t> maximum_size;
506          if (module->has_maximum_pages) {
507            maximum_size.emplace(module->maximum_pages);
508          }
509          type_value =
510              GetTypeForMemory(isolate, module->initial_pages, maximum_size,
511                               module->has_shared_memory);
512        }
513        export_kind = memory_string;
514        break;
515      case kExternalGlobal:
516        if (enabled_features.has_type_reflection()) {
517          auto& global = module->globals[exp.index];
518          type_value =
519              GetTypeForGlobal(isolate, global.mutability, global.type);
520        }
521        export_kind = global_string;
522        break;
523      case kExternalTag:
524        export_kind = tag_string;
525        break;
526      default:
527        UNREACHABLE();
528    }
529
530    Handle<JSObject> entry = factory->NewJSObject(object_function);
531
532    Handle<String> export_name =
533        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
534            isolate, module_object, exp.name, kNoInternalize);
535
536    JSObject::AddProperty(isolate, entry, name_string, export_name, NONE);
537    JSObject::AddProperty(isolate, entry, kind_string, export_kind, NONE);
538    if (!type_value.is_null()) {
539      JSObject::AddProperty(isolate, entry, type_string, type_value, NONE);
540    }
541
542    storage->set(index, *entry);
543  }
544
545  return array_object;
546}
547
548Handle<JSArray> GetCustomSections(Isolate* isolate,
549                                  Handle<WasmModuleObject> module_object,
550                                  Handle<String> name, ErrorThrower* thrower) {
551  Factory* factory = isolate->factory();
552
553  base::Vector<const uint8_t> wire_bytes =
554      module_object->native_module()->wire_bytes();
555  std::vector<CustomSectionOffset> custom_sections =
556      DecodeCustomSections(wire_bytes.begin(), wire_bytes.end());
557
558  std::vector<Handle<Object>> matching_sections;
559
560  // Gather matching sections.
561  for (auto& section : custom_sections) {
562    Handle<String> section_name =
563        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
564            isolate, module_object, section.name, kNoInternalize);
565
566    if (!name->Equals(*section_name)) continue;
567
568    // Make a copy of the payload data in the section.
569    size_t size = section.payload.length();
570    MaybeHandle<JSArrayBuffer> result =
571        isolate->factory()->NewJSArrayBufferAndBackingStore(
572            size, InitializedFlag::kUninitialized);
573    Handle<JSArrayBuffer> array_buffer;
574    if (!result.ToHandle(&array_buffer)) {
575      thrower->RangeError("out of memory allocating custom section data");
576      return Handle<JSArray>();
577    }
578    memcpy(array_buffer->backing_store(),
579           wire_bytes.begin() + section.payload.offset(),
580           section.payload.length());
581
582    matching_sections.push_back(array_buffer);
583  }
584
585  int num_custom_sections = static_cast<int>(matching_sections.size());
586  Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
587  Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections);
588  JSArray::SetContent(array_object, storage);
589  array_object->set_length(Smi::FromInt(num_custom_sections));
590
591  for (int i = 0; i < num_custom_sections; i++) {
592    storage->set(i, *matching_sections[i]);
593  }
594
595  return array_object;
596}
597
598// Get the source position from a given function index and byte offset,
599// for either asm.js or pure Wasm modules.
600int GetSourcePosition(const WasmModule* module, uint32_t func_index,
601                      uint32_t byte_offset, bool is_at_number_conversion) {
602  DCHECK_EQ(is_asmjs_module(module),
603            module->asm_js_offset_information != nullptr);
604  if (!is_asmjs_module(module)) {
605    // For non-asm.js modules, we just add the function's start offset
606    // to make a module-relative position.
607    return byte_offset + GetWasmFunctionOffset(module, func_index);
608  }
609
610  // asm.js modules have an additional offset table that must be searched.
611  return module->asm_js_offset_information->GetSourcePosition(
612      declared_function_index(module, func_index), byte_offset,
613      is_at_number_conversion);
614}
615
616namespace {
617template <typename T>
618inline size_t VectorSize(const std::vector<T>& vector) {
619  return sizeof(T) * vector.size();
620}
621}  // namespace
622
623size_t EstimateStoredSize(const WasmModule* module) {
624  return sizeof(WasmModule) + VectorSize(module->globals) +
625         (module->signature_zone ? module->signature_zone->allocation_size()
626                                 : 0) +
627         VectorSize(module->types) +
628         VectorSize(module->canonicalized_type_ids) +
629         VectorSize(module->functions) + VectorSize(module->data_segments) +
630         VectorSize(module->tables) + VectorSize(module->import_table) +
631         VectorSize(module->export_table) + VectorSize(module->tags) +
632         VectorSize(module->elem_segments);
633}
634
635size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig* sig,
636                      char delimiter) {
637  if (buffer.empty()) return 0;
638  size_t old_size = buffer.size();
639  auto append_char = [&buffer](char c) {
640    if (buffer.size() == 1) return;  // Keep last character for '\0'.
641    buffer[0] = c;
642    buffer += 1;
643  };
644  for (wasm::ValueType t : sig->parameters()) {
645    append_char(t.short_name());
646  }
647  append_char(delimiter);
648  for (wasm::ValueType t : sig->returns()) {
649    append_char(t.short_name());
650  }
651  buffer[0] = '\0';
652  return old_size - buffer.size();
653}
654
655}  // namespace wasm
656}  // namespace internal
657}  // namespace v8
658