wasm-module.h 21.6 KB
Newer Older
1 2 3 4
// Copyright 2015 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.

5 6 7 8
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif  // !V8_ENABLE_WEBASSEMBLY

9 10
#ifndef V8_WASM_WASM_MODULE_H_
#define V8_WASM_WASM_MODULE_H_
11

12
#include <map>
13 14
#include <memory>

15
#include "src/base/optional.h"
16
#include "src/base/platform/wrappers.h"
17
#include "src/base/vector.h"
18
#include "src/common/globals.h"
19
#include "src/handles/handles.h"
20
#include "src/wasm/branch-hint-map.h"
21
#include "src/wasm/signature-map.h"
22
#include "src/wasm/struct-types.h"
23
#include "src/wasm/wasm-constants.h"
24
#include "src/wasm/wasm-init-expr.h"
25

26
namespace v8 {
27

28 29
namespace internal {

30
class WasmModuleObject;
31 32

namespace wasm {
33

34
using WasmName = base::Vector<const char>;
35

36
struct AsmJsOffsets;
37 38
class ErrorThrower;

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
// Reference to a string in the wire bytes.
class WireBytesRef {
 public:
  WireBytesRef() : WireBytesRef(0, 0) {}
  WireBytesRef(uint32_t offset, uint32_t length)
      : offset_(offset), length_(length) {
    DCHECK_IMPLIES(offset_ == 0, length_ == 0);
    DCHECK_LE(offset_, offset_ + length_);  // no uint32_t overflow.
  }

  uint32_t offset() const { return offset_; }
  uint32_t length() const { return length_; }
  uint32_t end_offset() const { return offset_ + length_; }
  bool is_empty() const { return length_ == 0; }
  bool is_set() const { return offset_ != 0; }

 private:
  uint32_t offset_;
  uint32_t length_;
};

60
// Static representation of a wasm function.
61
struct WasmFunction {
62 63 64 65
  const FunctionSig* sig;  // signature of the function.
  uint32_t func_index;     // index into the function table.
  uint32_t sig_index;      // index into the signature table.
  WireBytesRef code;       // code of this function.
66 67 68 69
  // Required number of slots in a feedback vector. Marked {mutable} because
  // this is computed late (by Liftoff compilation), when the rest of the
  // {WasmFunction} is typically considered {const}.
  mutable int feedback_slots;
70 71
  bool imported;
  bool exported;
72
  bool declared;
titzer's avatar
titzer committed
73 74
};

75 76
// Static representation of a wasm global variable.
struct WasmGlobal {
77 78
  ValueType type;     // type of the global.
  bool mutability;    // {true} if mutable.
79
  WireBytesRef init;  // the initialization expression of the global.
80
  union {
81 82 83 84 85
    // Index of imported mutable global.
    uint32_t index;
    // Offset into global memory (if not imported & mutable). Expressed in bytes
    // for value-typed globals, and in tagged words for reference-typed globals.
    uint32_t offset;
86 87 88
  };
  bool imported;  // true if imported.
  bool exported;  // true if exported.
89 90
};

91 92 93
// Note: An exception tag signature only uses the params portion of a function
// signature.
using WasmTagSig = FunctionSig;
94

95 96 97
// Static representation of a wasm tag type.
struct WasmTag {
  explicit WasmTag(const WasmTagSig* sig) : sig(sig) {}
98
  const FunctionSig* ToFunctionSig() const { return sig; }
99

100
  const WasmTagSig* sig;  // type signature of the tag.
101 102
};

103 104
// Static representation of a wasm data segment.
struct WasmDataSegment {
105
  // Construct an active segment.
106
  explicit WasmDataSegment(WireBytesRef dest_addr)
107
      : dest_addr(std::move(dest_addr)), active(true) {}
108 109 110 111

  // Construct a passive segment, which has no dest_addr.
  WasmDataSegment() : active(false) {}

112
  WireBytesRef dest_addr;  // destination memory address of the data.
113
  WireBytesRef source;     // start offset in the module bytes.
114
  bool active = true;      // true if copied automatically during instantiation.
115 116
};

117 118
// Static representation of wasm element segment (table initializer).
struct WasmElemSegment {
119
  // Construct an active segment.
120
  WasmElemSegment(ValueType type, uint32_t table_index, WireBytesRef offset)
121
      : type(type),
122
        table_index(table_index),
123
        offset(std::move(offset)),
124
        status(kStatusActive) {}
125

126 127
  // Construct a passive or declarative segment, which has no table index or
  // offset.
128 129
  WasmElemSegment(ValueType type, bool declarative)
      : type(type),
130
        table_index(0),
131
        status(declarative ? kStatusDeclarative : kStatusPassive) {}
132

133 134 135 136 137 138 139 140 141
  // Construct a passive or declarative segment, which has no table index or
  // offset.
  WasmElemSegment()
      : type(kWasmBottom), table_index(0), status(kStatusActive) {}

  WasmElemSegment(const WasmElemSegment&) = delete;
  WasmElemSegment(WasmElemSegment&&) V8_NOEXCEPT = default;
  WasmElemSegment& operator=(const WasmElemSegment&) = delete;
  WasmElemSegment& operator=(WasmElemSegment&&) V8_NOEXCEPT = default;
142

143
  ValueType type;
144
  uint32_t table_index;
145
  WireBytesRef offset;
146 147 148 149 150 151 152
  struct Entry {
    enum Kind { kGlobalGetEntry, kRefFuncEntry, kRefNullEntry } kind;
    uint32_t index;
    Entry(Kind kind, uint32_t index) : kind(kind), index(index) {}
    Entry() : kind(kRefNullEntry), index(0) {}
  };
  std::vector<Entry> entries;
153 154 155 156 157
  enum Status {
    kStatusActive,      // copied automatically during instantiation.
    kStatusPassive,     // copied explicitly after instantiation.
    kStatusDeclarative  // purely declarative and never copied.
  } status;
158 159
};

160
// Static representation of a wasm import.
161
struct WasmImport {
162 163
  WireBytesRef module_name;   // module name.
  WireBytesRef field_name;    // import name.
164
  ImportExportKindCode kind;  // kind of the import.
165
  uint32_t index;             // index into the respective space.
166 167
};

168
// Static representation of a wasm export.
169
struct WasmExport {
170
  WireBytesRef name;          // exported name.
171
  ImportExportKindCode kind;  // kind of the export.
172 173 174 175 176 177 178
  uint32_t index;             // index into the respective space.
};

enum class WasmCompilationHintStrategy : uint8_t {
  kDefault = 0,
  kLazy = 1,
  kEager = 2,
179
  kLazyBaselineEagerTopTier = 3,
180 181 182 183
};

enum class WasmCompilationHintTier : uint8_t {
  kDefault = 0,
184 185
  kBaseline = 1,
  kOptimized = 2,
186 187 188 189 190
};

// Static representation of a wasm compilation hint
struct WasmCompilationHint {
  WasmCompilationHintStrategy strategy;
191 192
  WasmCompilationHintTier baseline_tier;
  WasmCompilationHintTier top_tier;
193 194
};

195 196 197 198 199
enum ModuleOrigin : uint8_t {
  kWasmOrigin,
  kAsmJsSloppyOrigin,
  kAsmJsStrictOrigin
};
200

201 202 203
#define SELECT_WASM_COUNTER(counters, origin, prefix, suffix)     \
  ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \
                           : (counters)->prefix##_asm_##suffix())
204

205
struct ModuleWireBytes;
206

207
class V8_EXPORT_PRIVATE LazilyGeneratedNames {
208
 public:
209
  WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes,
210
                                  uint32_t function_index) const;
211

212 213 214
  void AddForTesting(int function_index, WireBytesRef name);

 private:
215 216 217
  // {function_names_} are populated lazily after decoding, and
  // therefore need a mutex to protect concurrent modifications
  // from multiple {WasmModuleObject}.
218 219 220
  mutable base::Mutex mutex_;
  mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>>
      function_names_;
221 222
};

223 224
class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
 public:
225
  explicit AsmJsOffsetInformation(base::Vector<const byte> encoded_offsets);
226 227 228 229 230 231 232 233

  // Destructor defined in wasm-module.cc, where the definition of
  // {AsmJsOffsets} is available.
  ~AsmJsOffsetInformation();

  int GetSourcePosition(int func_index, int byte_offset,
                        bool is_at_number_conversion);

234 235
  std::pair<int, int> GetFunctionOffsets(int func_index);

236
 private:
237 238
  void EnsureDecodedOffsets();

239 240 241 242 243 244
  // The offset information table is decoded lazily, hence needs to be
  // protected against concurrent accesses.
  // Exactly one of the two fields below will be set at a time.
  mutable base::Mutex mutex_;

  // Holds the encoded offset table bytes.
245
  base::OwnedVector<const uint8_t> encoded_offsets_;
246 247 248 249 250

  // Holds the decoded offset table.
  std::unique_ptr<AsmJsOffsets> decoded_offsets_;
};

251 252 253 254 255 256 257 258 259 260 261
struct TypeDefinition {
  explicit TypeDefinition(const FunctionSig* sig) : function_sig(sig) {}
  explicit TypeDefinition(const StructType* type) : struct_type(type) {}
  explicit TypeDefinition(const ArrayType* type) : array_type(type) {}
  union {
    const FunctionSig* function_sig;
    const StructType* struct_type;
    const ArrayType* array_type;
  };
};

262 263 264 265 266 267
struct V8_EXPORT_PRIVATE WasmDebugSymbols {
  enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
  Type type = Type::None;
  WireBytesRef external_url;
};

268 269 270 271 272 273 274
struct CallSiteFeedback {
  int function_index;
  int absolute_call_frequency;
};
struct FunctionTypeFeedback {
  std::vector<CallSiteFeedback> feedback_vector;
  std::map<WasmCodePosition, int> positions;
275
  int tierup_priority = 0;
276
};
277
struct TypeFeedbackStorage {
278
  std::map<uint32_t, FunctionTypeFeedback> feedback_for_function;
279 280 281 282
  // Accesses to {feedback_for_function} are guarded by this mutex.
  base::Mutex mutex;
};

283 284
struct WasmTable;

285 286 287 288 289
// End of a chain of explicit supertypes.
constexpr uint32_t kGenericSuperType = 0xFFFFFFFE;
// Used for types that have no explicit supertype.
constexpr uint32_t kNoSuperType = 0xFFFFFFFF;

290
// Static representation of a module.
291
struct V8_EXPORT_PRIVATE WasmModule {
292
  std::unique_ptr<Zone> signature_zone;
293 294
  uint32_t initial_pages = 0;      // initial size of the memory in 64k pages
  uint32_t maximum_pages = 0;      // maximum size of the memory in 64k pages
295
  bool has_shared_memory = false;  // true if memory is a SharedArrayBuffer
296
  bool has_maximum_pages = false;  // true if there is a maximum memory size
297
  bool is_memory64 = false;        // true if the memory is 64 bit
298 299 300
  bool has_memory = false;         // true if the memory was defined or imported
  bool mem_export = false;         // true if the memory is exported
  int start_function_index = -1;   // start function, >= 0 if any
301

302
  std::vector<WasmGlobal> globals;
303 304
  // Size of the buffer required for all globals that are not imported and
  // mutable.
305 306
  uint32_t untagged_globals_buffer_size = 0;
  uint32_t tagged_globals_buffer_size = 0;
307
  uint32_t num_imported_mutable_globals = 0;
308
  uint32_t num_imported_functions = 0;
309
  uint32_t num_imported_tables = 0;
310
  uint32_t num_declared_functions = 0;  // excluding imported
311
  uint32_t num_exported_functions = 0;
312
  uint32_t num_declared_data_segments = 0;  // From the DataCount section.
313 314
  // Position and size of the code section (payload only, i.e. without section
  // ID and length).
315
  WireBytesRef code = {0, 0};
316
  WireBytesRef name = {0, 0};
317 318
  std::vector<TypeDefinition> types;  // by type index
  std::vector<uint8_t> type_kinds;    // by type index
319
  std::vector<uint32_t> supertypes;   // by type index
320 321
  // Map from each type index to the index of its corresponding canonical index.
  // Canonical indices do not correspond to types.
322
  // Note: right now, only functions are canonicalized, and arrays and structs
323
  // map to 0.
324
  std::vector<uint32_t> canonicalized_type_ids;
325 326 327

  bool has_type(uint32_t index) const { return index < types.size(); }

328
  void add_signature(const FunctionSig* sig, uint32_t supertype) {
329 330
    types.push_back(TypeDefinition(sig));
    type_kinds.push_back(kWasmFunctionTypeCode);
331
    supertypes.push_back(supertype);
332
    uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
333
    canonicalized_type_ids.push_back(canonical_id);
334 335 336 337
  }
  bool has_signature(uint32_t index) const {
    return index < types.size() && type_kinds[index] == kWasmFunctionTypeCode;
  }
338 339 340 341 342
  const FunctionSig* signature(uint32_t index) const {
    DCHECK(has_signature(index));
    return types[index].function_sig;
  }

343
  void add_struct_type(const StructType* type, uint32_t supertype) {
344 345
    types.push_back(TypeDefinition(type));
    type_kinds.push_back(kWasmStructTypeCode);
346
    supertypes.push_back(supertype);
347 348
    // No canonicalization for structs.
    canonicalized_type_ids.push_back(0);
349 350 351 352
  }
  bool has_struct(uint32_t index) const {
    return index < types.size() && type_kinds[index] == kWasmStructTypeCode;
  }
353 354 355 356 357
  const StructType* struct_type(uint32_t index) const {
    DCHECK(has_struct(index));
    return types[index].struct_type;
  }

358
  void add_array_type(const ArrayType* type, uint32_t supertype) {
359 360
    types.push_back(TypeDefinition(type));
    type_kinds.push_back(kWasmArrayTypeCode);
361
    supertypes.push_back(supertype);
362 363
    // No canonicalization for arrays.
    canonicalized_type_ids.push_back(0);
364 365 366 367
  }
  bool has_array(uint32_t index) const {
    return index < types.size() && type_kinds[index] == kWasmArrayTypeCode;
  }
368 369 370 371
  const ArrayType* array_type(uint32_t index) const {
    DCHECK(has_array(index));
    return types[index].array_type;
  }
372

373 374 375 376 377 378 379 380
  uint32_t supertype(uint32_t index) const {
    DCHECK(index < supertypes.size());
    return supertypes[index];
  }
  bool has_supertype(uint32_t index) const {
    return supertype(index) != kNoSuperType;
  }

381 382
  std::vector<WasmFunction> functions;
  std::vector<WasmDataSegment> data_segments;
383
  std::vector<WasmTable> tables;
384 385
  std::vector<WasmImport> import_table;
  std::vector<WasmExport> export_table;
386
  std::vector<WasmTag> tags;
387
  std::vector<WasmElemSegment> elem_segments;
388
  std::vector<WasmCompilationHint> compilation_hints;
389
  BranchHintInfo branch_hints;
390
  SignatureMap signature_map;  // canonicalizing map for signature indexes.
391 392 393
  // Entries in this storage are short-lived: when tier-up of a function is
  // scheduled, an entry is placed; the Turbofan graph builder consumes it.
  mutable TypeFeedbackStorage type_feedback;
394

395
  ModuleOrigin origin = kWasmOrigin;  // origin of the module
396
  LazilyGeneratedNames lazily_generated_names;
397
  WasmDebugSymbols debug_symbols;
398

399 400 401 402
  // Asm.js source position information. Only available for modules compiled
  // from asm.js.
  std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information;

403
  explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
404
  WasmModule(const WasmModule&) = delete;
405
  ~WasmModule();
406
  WasmModule& operator=(const WasmModule&) = delete;
407 408
};

409 410 411 412 413 414 415 416
// Static representation of a wasm indirect call table.
struct WasmTable {
  MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable);

  // 'module' can be nullptr
  // TODO(9495): Update this function as more table types are supported, or
  // remove it completely when all reference types are allowed.
  static bool IsValidTableType(ValueType type, const WasmModule* module) {
417
    if (!type.is_object_reference()) return false;
418 419 420 421 422 423
    HeapType heap_type = type.heap_type();
    return heap_type == HeapType::kFunc || heap_type == HeapType::kExtern ||
           (module != nullptr && heap_type.is_index() &&
            module->has_signature(heap_type.ref_index()));
  }

424
  ValueType type = kWasmVoid;     // table type.
425 426 427 428 429
  uint32_t initial_size = 0;      // initial table size.
  uint32_t maximum_size = 0;      // maximum table size.
  bool has_maximum_size = false;  // true if there is a maximum size.
  bool imported = false;          // true if imported.
  bool exported = false;          // true if exported.
430
  WireBytesRef initial_value;
431 432
};

433 434 435 436
inline bool is_asmjs_module(const WasmModule* module) {
  return module->origin != kWasmOrigin;
}

437
size_t EstimateStoredSize(const WasmModule* module);
438

439 440 441 442
// Returns the number of possible export wrappers for a given module.
V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);

// Returns the wrapper index for a function in {module} with signature {sig}
443 444
// or {sig_index} and origin defined by {is_import}.
// Prefer to use the {sig_index} consuming version, as it is much faster.
445 446
int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
                          bool is_import);
447 448
int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
                          bool is_import);
449

450 451 452 453 454
// Return the byte offset of the function identified by the given index.
// The offset will be relative to the start of the module bytes.
// Returns -1 if the function index is invalid.
int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index);

455
// Returns the function containing the given byte offset.
456 457
// Returns -1 if the byte offset is not contained in any
// function of this module.
458 459
int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);

460 461 462 463 464
// Returns the function containing the given byte offset.
// Will return preceding function if the byte offset is not
// contained within a function.
int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);

465 466 467 468
// Gets the explicitly defined subtyping depth for the given type.
// Returns 0 if the type has no explicit supertype.
// The result is capped to {kV8MaxRttSubtypingDepth + 1}.
// Invalid cyclic hierarchies will return -1.
469 470
V8_EXPORT_PRIVATE int GetSubtypingDepth(const WasmModule* module,
                                        uint32_t type_index);
471

472 473 474 475 476
// Interface to the storage (wire bytes) of a wasm module.
// It is illegal for anyone receiving a ModuleWireBytes to store pointers based
// on module_bytes, as this storage is only guaranteed to be alive as long as
// this struct is alive.
struct V8_EXPORT_PRIVATE ModuleWireBytes {
477
  explicit ModuleWireBytes(base::Vector<const byte> module_bytes)
478
      : module_bytes_(module_bytes) {}
479
  ModuleWireBytes(const byte* start, const byte* end)
480
      : module_bytes_(start, static_cast<int>(end - start)) {
481 482 483 484
    DCHECK_GE(kMaxInt, end - start);
  }

  // Get a string stored in the module bytes representing a name.
485
  WasmName GetNameOrNull(WireBytesRef ref) const;
486 487

  // Get a string stored in the module bytes representing a function name.
488 489
  WasmName GetNameOrNull(const WasmFunction* function,
                         const WasmModule* module) const;
490

491 492
  // Checks the given reference is contained within the module bytes.
  bool BoundsCheck(WireBytesRef ref) const {
493
    uint32_t size = static_cast<uint32_t>(module_bytes_.length());
494
    return ref.offset() <= size && ref.length() <= size - ref.offset();
495
  }
496

497 498
  base::Vector<const byte> GetFunctionBytes(
      const WasmFunction* function) const {
499 500
    return module_bytes_.SubVector(function->code.offset(),
                                   function->code.end_offset());
501 502
  }

503
  base::Vector<const byte> module_bytes() const { return module_bytes_; }
504
  const byte* start() const { return module_bytes_.begin(); }
505
  const byte* end() const { return module_bytes_.end(); }
506
  size_t length() const { return module_bytes_.length(); }
507 508

 private:
509
  base::Vector<const byte> module_bytes_;
510 511
};

512 513
// A helper for printing out the names of functions.
struct WasmFunctionName {
514 515
  WasmFunctionName(const WasmFunction* function, WasmName name)
      : function_(function), name_(name) {}
516

517
  const WasmFunction* function_;
518
  const WasmName name_;
519 520 521
};

std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
522

523 524 525
V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
                                            Handle<Context> context);

526 527
Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig,
                                    bool for_exception = false);
528 529
Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
                                  ValueType type);
530
Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
531 532
                                  base::Optional<uint32_t> max_size,
                                  bool shared);
533 534 535
Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
                                 uint32_t min_size,
                                 base::Optional<uint32_t> max_size);
536 537 538 539 540
Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module);
Handle<JSArray> GetCustomSections(Isolate* isolate,
                                  Handle<WasmModuleObject> module,
                                  Handle<String> name, ErrorThrower* thrower);
541

542 543 544 545 546
// Get the source position from a given function index and byte offset,
// for either asm.js or pure Wasm modules.
int GetSourcePosition(const WasmModule*, uint32_t func_index,
                      uint32_t byte_offset, bool is_at_number_conversion);

547 548 549 550 551 552 553 554 555
// Translate function index to the index relative to the first declared (i.e.
// non-imported) function.
inline int declared_function_index(const WasmModule* module, int func_index) {
  DCHECK_LE(module->num_imported_functions, func_index);
  int declared_idx = func_index - module->num_imported_functions;
  DCHECK_GT(module->num_declared_functions, declared_idx);
  return declared_idx;
}

556 557 558 559 560 561 562 563 564 565 566
// TruncatedUserString makes it easy to output names up to a certain length, and
// output a truncation followed by '...' if they exceed a limit.
// Use like this:
//   TruncatedUserString<> name (pc, len);
//   printf("... %.*s ...", name.length(), name.start())
template <int kMaxLen = 50>
class TruncatedUserString {
  static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)");

 public:
  template <typename T>
567
  explicit TruncatedUserString(base::Vector<T> name)
568
      : TruncatedUserString(name.begin(), name.length()) {}
569 570 571 572 573 574 575

  TruncatedUserString(const byte* start, size_t len)
      : TruncatedUserString(reinterpret_cast<const char*>(start), len) {}

  TruncatedUserString(const char* start, size_t len)
      : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) {
    if (len > static_cast<size_t>(kMaxLen)) {
576
      memcpy(buffer_, start, kMaxLen - 3);
577 578 579 580 581 582 583 584 585 586 587
      memset(buffer_ + kMaxLen - 3, '.', 3);
      start_ = buffer_;
    }
  }

  const char* start() const { return start_; }

  int length() const { return length_; }

 private:
  const char* start_;
588
  const int length_;
589 590 591
  char buffer_[kMaxLen];
};

592 593 594 595
// Print the signature into the given {buffer}, using {delimiter} as separator
// between parameter types and return types. If {buffer} is non-empty, it will
// be null-terminated, even if the signature is cut off. Returns the number of
// characters written, excluding the terminating null-byte.
596
size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*,
597
                      char delimiter = ':');
598

599 600 601 602
}  // namespace wasm
}  // namespace internal
}  // namespace v8

603
#endif  // V8_WASM_WASM_MODULE_H_