d66cc11c2f
The JS API constructor was renamed to "WebAssembly.Tag" to match the spec: https://github.com/WebAssembly/exception-handling/issues/159 Rename "exception" to "tag" throughout the codebase for consistency with the JS API, and to match the spec terminology (e.g. "tag section"). R=clemensb@chromium.org,nicohartmann@chromium.org Bug: v8:11992 Change-Id: I63f9f3101abfeefd49117461bd59c594ca5dab70 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3053583 Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Commit-Queue: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#75994}
552 lines
20 KiB
C++
552 lines
20 KiB
C++
// 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.
|
|
|
|
#if !V8_ENABLE_WEBASSEMBLY
|
|
#error This header should only be included if WebAssembly is enabled.
|
|
#endif // !V8_ENABLE_WEBASSEMBLY
|
|
|
|
#ifndef V8_WASM_WASM_MODULE_H_
|
|
#define V8_WASM_WASM_MODULE_H_
|
|
|
|
#include <memory>
|
|
|
|
#include "src/base/optional.h"
|
|
#include "src/base/platform/wrappers.h"
|
|
#include "src/base/vector.h"
|
|
#include "src/common/globals.h"
|
|
#include "src/handles/handles.h"
|
|
#include "src/wasm/branch-hint-map.h"
|
|
#include "src/wasm/signature-map.h"
|
|
#include "src/wasm/struct-types.h"
|
|
#include "src/wasm/wasm-constants.h"
|
|
#include "src/wasm/wasm-init-expr.h"
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
class WasmModuleObject;
|
|
|
|
namespace wasm {
|
|
|
|
using WasmName = base::Vector<const char>;
|
|
|
|
struct AsmJsOffsets;
|
|
class ErrorThrower;
|
|
|
|
// 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_;
|
|
};
|
|
|
|
// Static representation of a wasm function.
|
|
struct WasmFunction {
|
|
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.
|
|
bool imported;
|
|
bool exported;
|
|
bool declared;
|
|
};
|
|
|
|
// Static representation of a wasm global variable.
|
|
struct WasmGlobal {
|
|
ValueType type; // type of the global.
|
|
bool mutability; // {true} if mutable.
|
|
WireBytesRef init; // the initialization expression of the global.
|
|
union {
|
|
uint32_t index; // index of imported mutable global.
|
|
uint32_t offset; // offset into global memory (if not imported & mutable).
|
|
};
|
|
bool imported; // true if imported.
|
|
bool exported; // true if exported.
|
|
};
|
|
|
|
// Note: An exception tag signature only uses the params portion of a function
|
|
// signature.
|
|
using WasmTagSig = FunctionSig;
|
|
|
|
// Static representation of a wasm tag type.
|
|
struct WasmTag {
|
|
explicit WasmTag(const WasmTagSig* sig) : sig(sig) {}
|
|
const FunctionSig* ToFunctionSig() const { return sig; }
|
|
|
|
const WasmTagSig* sig; // type signature of the tag.
|
|
};
|
|
|
|
// Static representation of a wasm data segment.
|
|
struct WasmDataSegment {
|
|
// Construct an active segment.
|
|
explicit WasmDataSegment(WireBytesRef dest_addr)
|
|
: dest_addr(std::move(dest_addr)), active(true) {}
|
|
|
|
// Construct a passive segment, which has no dest_addr.
|
|
WasmDataSegment() : active(false) {}
|
|
|
|
WireBytesRef dest_addr; // destination memory address of the data.
|
|
WireBytesRef source; // start offset in the module bytes.
|
|
bool active = true; // true if copied automatically during instantiation.
|
|
};
|
|
|
|
// Static representation of wasm element segment (table initializer).
|
|
struct WasmElemSegment {
|
|
// Construct an active segment.
|
|
WasmElemSegment(ValueType type, uint32_t table_index, WireBytesRef offset)
|
|
: type(type),
|
|
table_index(table_index),
|
|
offset(std::move(offset)),
|
|
status(kStatusActive) {}
|
|
|
|
// Construct a passive or declarative segment, which has no table index or
|
|
// offset.
|
|
WasmElemSegment(ValueType type, bool declarative)
|
|
: type(type),
|
|
table_index(0),
|
|
status(declarative ? kStatusDeclarative : kStatusPassive) {}
|
|
|
|
// 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;
|
|
|
|
ValueType type;
|
|
uint32_t table_index;
|
|
WireBytesRef offset;
|
|
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;
|
|
enum Status {
|
|
kStatusActive, // copied automatically during instantiation.
|
|
kStatusPassive, // copied explicitly after instantiation.
|
|
kStatusDeclarative // purely declarative and never copied.
|
|
} status;
|
|
};
|
|
|
|
// Static representation of a wasm import.
|
|
struct WasmImport {
|
|
WireBytesRef module_name; // module name.
|
|
WireBytesRef field_name; // import name.
|
|
ImportExportKindCode kind; // kind of the import.
|
|
uint32_t index; // index into the respective space.
|
|
};
|
|
|
|
// Static representation of a wasm export.
|
|
struct WasmExport {
|
|
WireBytesRef name; // exported name.
|
|
ImportExportKindCode kind; // kind of the export.
|
|
uint32_t index; // index into the respective space.
|
|
};
|
|
|
|
enum class WasmCompilationHintStrategy : uint8_t {
|
|
kDefault = 0,
|
|
kLazy = 1,
|
|
kEager = 2,
|
|
kLazyBaselineEagerTopTier = 3,
|
|
};
|
|
|
|
enum class WasmCompilationHintTier : uint8_t {
|
|
kDefault = 0,
|
|
kBaseline = 1,
|
|
kOptimized = 2,
|
|
};
|
|
|
|
// Static representation of a wasm compilation hint
|
|
struct WasmCompilationHint {
|
|
WasmCompilationHintStrategy strategy;
|
|
WasmCompilationHintTier baseline_tier;
|
|
WasmCompilationHintTier top_tier;
|
|
};
|
|
|
|
enum ModuleOrigin : uint8_t {
|
|
kWasmOrigin,
|
|
kAsmJsSloppyOrigin,
|
|
kAsmJsStrictOrigin
|
|
};
|
|
|
|
#define SELECT_WASM_COUNTER(counters, origin, prefix, suffix) \
|
|
((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \
|
|
: (counters)->prefix##_asm_##suffix())
|
|
|
|
struct ModuleWireBytes;
|
|
|
|
class V8_EXPORT_PRIVATE LazilyGeneratedNames {
|
|
public:
|
|
WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes,
|
|
uint32_t function_index) const;
|
|
|
|
void AddForTesting(int function_index, WireBytesRef name);
|
|
|
|
private:
|
|
// {function_names_} are populated lazily after decoding, and
|
|
// therefore need a mutex to protect concurrent modifications
|
|
// from multiple {WasmModuleObject}.
|
|
mutable base::Mutex mutex_;
|
|
mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>>
|
|
function_names_;
|
|
};
|
|
|
|
class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
|
|
public:
|
|
explicit AsmJsOffsetInformation(base::Vector<const byte> encoded_offsets);
|
|
|
|
// 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);
|
|
|
|
std::pair<int, int> GetFunctionOffsets(int func_index);
|
|
|
|
private:
|
|
void EnsureDecodedOffsets();
|
|
|
|
// 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.
|
|
base::OwnedVector<const uint8_t> encoded_offsets_;
|
|
|
|
// Holds the decoded offset table.
|
|
std::unique_ptr<AsmJsOffsets> decoded_offsets_;
|
|
};
|
|
|
|
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;
|
|
};
|
|
};
|
|
|
|
struct V8_EXPORT_PRIVATE WasmDebugSymbols {
|
|
enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
|
|
Type type = Type::None;
|
|
WireBytesRef external_url;
|
|
};
|
|
|
|
struct WasmTable;
|
|
|
|
// Static representation of a module.
|
|
struct V8_EXPORT_PRIVATE WasmModule {
|
|
std::unique_ptr<Zone> signature_zone;
|
|
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
|
|
bool has_shared_memory = false; // true if memory is a SharedArrayBuffer
|
|
bool has_maximum_pages = false; // true if there is a maximum memory size
|
|
bool is_memory64 = false; // true if the memory is 64 bit
|
|
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
|
|
|
|
std::vector<WasmGlobal> globals;
|
|
// Size of the buffer required for all globals that are not imported and
|
|
// mutable.
|
|
uint32_t untagged_globals_buffer_size = 0;
|
|
uint32_t tagged_globals_buffer_size = 0;
|
|
uint32_t num_imported_mutable_globals = 0;
|
|
uint32_t num_imported_functions = 0;
|
|
uint32_t num_imported_tables = 0;
|
|
uint32_t num_declared_functions = 0; // excluding imported
|
|
uint32_t num_exported_functions = 0;
|
|
uint32_t num_declared_data_segments = 0; // From the DataCount section.
|
|
// Position and size of the code section (payload only, i.e. without section
|
|
// ID and length).
|
|
WireBytesRef code = {0, 0};
|
|
WireBytesRef name = {0, 0};
|
|
std::vector<TypeDefinition> types; // by type index
|
|
std::vector<uint8_t> type_kinds; // by type index
|
|
// Map from each type index to the index of its corresponding canonical type.
|
|
// Note: right now, only functions are canonicalized, and arrays and structs
|
|
// map to themselves.
|
|
std::vector<uint32_t> canonicalized_type_ids;
|
|
|
|
bool has_type(uint32_t index) const { return index < types.size(); }
|
|
|
|
void add_signature(const FunctionSig* sig) {
|
|
types.push_back(TypeDefinition(sig));
|
|
type_kinds.push_back(kWasmFunctionTypeCode);
|
|
uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
|
|
canonicalized_type_ids.push_back(canonical_id);
|
|
}
|
|
bool has_signature(uint32_t index) const {
|
|
return index < types.size() && type_kinds[index] == kWasmFunctionTypeCode;
|
|
}
|
|
const FunctionSig* signature(uint32_t index) const {
|
|
DCHECK(has_signature(index));
|
|
return types[index].function_sig;
|
|
}
|
|
|
|
void add_struct_type(const StructType* type) {
|
|
types.push_back(TypeDefinition(type));
|
|
type_kinds.push_back(kWasmStructTypeCode);
|
|
// No canonicalization for structs.
|
|
canonicalized_type_ids.push_back(0);
|
|
}
|
|
bool has_struct(uint32_t index) const {
|
|
return index < types.size() && type_kinds[index] == kWasmStructTypeCode;
|
|
}
|
|
const StructType* struct_type(uint32_t index) const {
|
|
DCHECK(has_struct(index));
|
|
return types[index].struct_type;
|
|
}
|
|
|
|
void add_array_type(const ArrayType* type) {
|
|
types.push_back(TypeDefinition(type));
|
|
type_kinds.push_back(kWasmArrayTypeCode);
|
|
// No canonicalization for arrays.
|
|
canonicalized_type_ids.push_back(0);
|
|
}
|
|
bool has_array(uint32_t index) const {
|
|
return index < types.size() && type_kinds[index] == kWasmArrayTypeCode;
|
|
}
|
|
const ArrayType* array_type(uint32_t index) const {
|
|
DCHECK(has_array(index));
|
|
return types[index].array_type;
|
|
}
|
|
|
|
std::vector<WasmFunction> functions;
|
|
std::vector<WasmDataSegment> data_segments;
|
|
std::vector<WasmTable> tables;
|
|
std::vector<WasmImport> import_table;
|
|
std::vector<WasmExport> export_table;
|
|
std::vector<WasmTag> tags;
|
|
std::vector<WasmElemSegment> elem_segments;
|
|
std::vector<WasmCompilationHint> compilation_hints;
|
|
BranchHintInfo branch_hints;
|
|
SignatureMap signature_map; // canonicalizing map for signature indexes.
|
|
|
|
ModuleOrigin origin = kWasmOrigin; // origin of the module
|
|
LazilyGeneratedNames lazily_generated_names;
|
|
WasmDebugSymbols debug_symbols;
|
|
|
|
// Asm.js source position information. Only available for modules compiled
|
|
// from asm.js.
|
|
std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information;
|
|
|
|
explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
|
|
WasmModule(const WasmModule&) = delete;
|
|
~WasmModule();
|
|
WasmModule& operator=(const WasmModule&) = delete;
|
|
};
|
|
|
|
// 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) {
|
|
if (!type.is_object_reference()) return false;
|
|
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()));
|
|
}
|
|
|
|
ValueType type = kWasmVoid; // table type.
|
|
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.
|
|
WireBytesRef initial_value;
|
|
};
|
|
|
|
inline bool is_asmjs_module(const WasmModule* module) {
|
|
return module->origin != kWasmOrigin;
|
|
}
|
|
|
|
size_t EstimateStoredSize(const WasmModule* module);
|
|
|
|
// 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}
|
|
// or {sig_index} and origin defined by {is_import}.
|
|
// Prefer to use the {sig_index} consuming version, as it is much faster.
|
|
int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
|
|
bool is_import);
|
|
int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
|
|
bool is_import);
|
|
|
|
// 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);
|
|
|
|
// Returns the function containing the given byte offset.
|
|
// Returns -1 if the byte offset is not contained in any
|
|
// function of this module.
|
|
int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
|
|
|
|
// 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);
|
|
|
|
// 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 {
|
|
explicit ModuleWireBytes(base::Vector<const byte> module_bytes)
|
|
: module_bytes_(module_bytes) {}
|
|
ModuleWireBytes(const byte* start, const byte* end)
|
|
: module_bytes_(start, static_cast<int>(end - start)) {
|
|
DCHECK_GE(kMaxInt, end - start);
|
|
}
|
|
|
|
// Get a string stored in the module bytes representing a name.
|
|
WasmName GetNameOrNull(WireBytesRef ref) const;
|
|
|
|
// Get a string stored in the module bytes representing a function name.
|
|
WasmName GetNameOrNull(const WasmFunction* function,
|
|
const WasmModule* module) const;
|
|
|
|
// Checks the given reference is contained within the module bytes.
|
|
bool BoundsCheck(WireBytesRef ref) const {
|
|
uint32_t size = static_cast<uint32_t>(module_bytes_.length());
|
|
return ref.offset() <= size && ref.length() <= size - ref.offset();
|
|
}
|
|
|
|
base::Vector<const byte> GetFunctionBytes(
|
|
const WasmFunction* function) const {
|
|
return module_bytes_.SubVector(function->code.offset(),
|
|
function->code.end_offset());
|
|
}
|
|
|
|
base::Vector<const byte> module_bytes() const { return module_bytes_; }
|
|
const byte* start() const { return module_bytes_.begin(); }
|
|
const byte* end() const { return module_bytes_.end(); }
|
|
size_t length() const { return module_bytes_.length(); }
|
|
|
|
private:
|
|
base::Vector<const byte> module_bytes_;
|
|
};
|
|
|
|
// A helper for printing out the names of functions.
|
|
struct WasmFunctionName {
|
|
WasmFunctionName(const WasmFunction* function, WasmName name)
|
|
: function_(function), name_(name) {}
|
|
|
|
const WasmFunction* function_;
|
|
const WasmName name_;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
|
|
|
|
V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
|
|
Handle<Context> context);
|
|
|
|
Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig,
|
|
bool for_exception = false);
|
|
Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
|
|
ValueType type);
|
|
Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
|
|
base::Optional<uint32_t> max_size);
|
|
Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
|
|
uint32_t min_size,
|
|
base::Optional<uint32_t> max_size);
|
|
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);
|
|
|
|
// 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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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>
|
|
explicit TruncatedUserString(base::Vector<T> name)
|
|
: TruncatedUserString(name.begin(), name.length()) {}
|
|
|
|
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)) {
|
|
memcpy(buffer_, start, kMaxLen - 3);
|
|
memset(buffer_ + kMaxLen - 3, '.', 3);
|
|
start_ = buffer_;
|
|
}
|
|
}
|
|
|
|
const char* start() const { return start_; }
|
|
|
|
int length() const { return length_; }
|
|
|
|
private:
|
|
const char* start_;
|
|
const int length_;
|
|
char buffer_[kMaxLen];
|
|
};
|
|
|
|
// 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.
|
|
size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*,
|
|
char delimiter = ':');
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_WASM_WASM_MODULE_H_
|