// 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.
#include "src/wasm/module-decoder.h"
#include "src/base/functional.h"
#include "src/base/platform/platform.h"
#include "src/macro-assembler.h"
#include "src/objects.h"
#include "src/v8.h"
#include "src/wasm/decoder.h"
namespace v8 {
namespace internal {
namespace wasm {
#if DEBUG
#define TRACE(...) \
do { \
if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \
} while (false)
#else
#define TRACE(...)
#endif
namespace {
// The main logic for decoding the bytes of a module.
class ModuleDecoder : public Decoder {
public:
ModuleDecoder(Zone* zone, const byte* module_start, const byte* module_end,
ModuleOrigin origin)
: Decoder(module_start, module_end), module_zone(zone), origin_(origin) {
result_.start = start_;
if (limit_ < start_) {
error(start_, "end is less than start");
limit_ = start_;
}
}
virtual void onFirstError() {
pc_ = limit_; // On error, terminate section decoding loop.
}
static void DumpModule(WasmModule* module, ModuleResult result) {
std::string path;
if (FLAG_dump_wasm_module_path) {
path = FLAG_dump_wasm_module_path;
if (path.size() &&
!base::OS::isDirectorySeparator(path[path.size() - 1])) {
path += base::OS::DirectorySeparator();
}
}
// File are named `HASH.{ok,failed}.wasm`.
size_t hash = base::hash_range(module->module_start, module->module_end);
char buf[32] = {'\0'};
#if V8_OS_WIN && _MSC_VER < 1900
#define snprintf sprintf_s
#endif
snprintf(buf, sizeof(buf) - 1, "%016zx.%s.wasm", hash,
result.ok() ? "ok" : "failed");
std::string name(buf);
if (FILE* wasm_file = base::OS::FOpen((path + name).c_str(), "wb")) {
fwrite(module->module_start, module->module_end - module->module_start, 1,
wasm_file);
fclose(wasm_file);
}
}
// Decodes an entire module.
ModuleResult DecodeModule(WasmModule* module, bool verify_functions = true) {
pc_ = start_;
module->module_start = start_;
module->module_end = limit_;
module->min_mem_pages = 0;
module->max_mem_pages = 0;
module->mem_export = false;
module->mem_external = false;
module->origin = origin_;
const byte* pos = pc_;
int current_order = 0;
uint32_t magic_word = consume_u32("wasm magic");
#define BYTES(x) (x & 0xff), (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff
if (magic_word != kWasmMagic) {
error(pos, pos,
"expected magic word %02x %02x %02x %02x, "
"found %02x %02x %02x %02x",
BYTES(kWasmMagic), BYTES(magic_word));
goto done;
}
pos = pc_;
{
uint32_t magic_version = consume_u32("wasm version");
if (magic_version != kWasmVersion) {
error(pos, pos,
"expected version %02x %02x %02x %02x, "
"found %02x %02x %02x %02x",
BYTES(kWasmVersion), BYTES(magic_version));
goto done;
}
}
// Decode the module sections.
while (pc_ < limit_) {
TRACE("DecodeSection\n");
pos = pc_;
// Read the section name.
uint32_t string_length = consume_u32v("section name length");
const byte* section_name_start = pc_;
consume_bytes(string_length);
if (failed()) {
TRACE("Section name of length %u couldn't be read\n", string_length);
break;
}
TRACE(" +%d section name : \"%.*s\"\n",
static_cast<int>(section_name_start - start_),
string_length < 20 ? string_length : 20, section_name_start);
WasmSection::Code section =
WasmSection::lookup(section_name_start, string_length);
// Read and check the section size.
uint32_t section_length = consume_u32v("section length");
if (!checkAvailable(section_length)) {
// The section would extend beyond the end of the module.
break;
}
const byte* section_start = pc_;
const byte* expected_section_end = pc_ + section_length;
current_order = CheckSectionOrder(current_order, section);
switch (section) {
case WasmSection::Code::End:
// Terminate section decoding.
limit_ = pc_;
break;
case WasmSection::Code::Memory: {
module->min_mem_pages = consume_u32v("min memory");
module->max_mem_pages = consume_u32v("max memory");
module->mem_export = consume_u8("export memory") != 0;
break;
}
case WasmSection::Code::Signatures: {
uint32_t signatures_count = consume_u32v("signatures count");
module->signatures.reserve(SafeReserve(signatures_count));
// Decode signatures.
for (uint32_t i = 0; i < signatures_count; ++i) {
if (failed()) break;
TRACE("DecodeSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
FunctionSig* s = consume_sig();
module->signatures.push_back(s);
}
break;
}
case WasmSection::Code::FunctionSignatures: {
uint32_t functions_count = consume_u32v("functions count");
module->functions.reserve(SafeReserve(functions_count));
for (uint32_t i = 0; i < functions_count; ++i) {
module->functions.push_back({nullptr, // sig
i, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0}); // code_end_offset
WasmFunction* function = &module->functions.back();
function->sig_index = consume_sig_index(module, &function->sig);
}
break;
}
case WasmSection::Code::FunctionBodies: {
const byte* pos = pc_;
uint32_t functions_count = consume_u32v("functions count");
if (functions_count != module->functions.size()) {
error(pos, pos, "function body count %u mismatch (%u expected)",
functions_count,
static_cast<uint32_t>(module->functions.size()));
break;
}
for (uint32_t i = 0; i < functions_count; ++i) {
WasmFunction* function = &module->functions[i];
uint32_t size = consume_u32v("body size");
function->code_start_offset = pc_offset();
function->code_end_offset = pc_offset() + size;
TRACE(" +%d %-20s: (%d bytes)\n", pc_offset(), "function body",
size);
pc_ += size;
if (pc_ > limit_) {
error(pc_, "function body extends beyond end of file");
}
}
break;
}
case WasmSection::Code::Names: {
const byte* pos = pc_;
uint32_t functions_count = consume_u32v("functions count");
if (functions_count != module->functions.size()) {
error(pos, pos, "function name count %u mismatch (%u expected)",
functions_count,
static_cast<uint32_t>(module->functions.size()));
break;
}
for (uint32_t i = 0; i < functions_count; ++i) {
WasmFunction* function = &module->functions[i];
function->name_offset =
consume_string(&function->name_length, false);
uint32_t local_names_count = consume_u32v("local names count");
for (uint32_t j = 0; j < local_names_count; j++) {
uint32_t unused = 0;
uint32_t offset = consume_string(&unused, false);
USE(unused);
USE(offset);
}
}
break;
}
case WasmSection::Code::Globals: {
uint32_t globals_count = consume_u32v("globals count");
module->globals.reserve(SafeReserve(globals_count));
// Decode globals.
for (uint32_t i = 0; i < globals_count; ++i) {
if (failed()) break;
TRACE("DecodeGlobal[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module->globals.push_back({0, 0, MachineType::Int32(), 0, false});
WasmGlobal* global = &module->globals.back();
DecodeGlobalInModule(global);
}
break;
}
case WasmSection::Code::DataSegments: {
uint32_t data_segments_count = consume_u32v("data segments count");
module->data_segments.reserve(SafeReserve(data_segments_count));
// Decode data segments.
for (uint32_t i = 0; i < data_segments_count; ++i) {
if (failed()) break;
TRACE("DecodeDataSegment[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module->data_segments.push_back({0, // dest_addr
0, // source_offset
0, // source_size
false}); // init
WasmDataSegment* segment = &module->data_segments.back();
DecodeDataSegmentInModule(module, segment);
}
break;
}
case WasmSection::Code::FunctionTablePad: {
if (!FLAG_wasm_jit_prototype) {
error("FunctionTablePad section without jiting enabled");
}
// An indirect function table requires functions first.
module->indirect_table_size = consume_u32v("indirect entry count");
if (module->indirect_table_size > 0 &&
module->indirect_table_size < module->function_table.size()) {
error("more predefined indirect entries than table can hold");
}
break;
}
case WasmSection::Code::FunctionTable: {
// An indirect function table requires functions first.
CheckForFunctions(module, section);
uint32_t function_table_count = consume_u32v("function table count");
module->function_table.reserve(SafeReserve(function_table_count));
// Decode function table.
for (uint32_t i = 0; i < function_table_count; ++i) {
if (failed()) break;
TRACE("DecodeFunctionTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
uint16_t index = consume_u32v();
if (index >= module->functions.size()) {
error(pc_ - 2, "invalid function index");
break;
}
module->function_table.push_back(index);
}
if (module->indirect_table_size > 0 &&
module->indirect_table_size < module->function_table.size()) {
error("more predefined indirect entries than table can hold");
}
break;
}
case WasmSection::Code::StartFunction: {
// Declares a start function for a module.
CheckForFunctions(module, section);
if (module->start_function_index >= 0) {
error("start function already declared");
break;
}
WasmFunction* func;
const byte* pos = pc_;
module->start_function_index = consume_func_index(module, &func);
if (func && func->sig->parameter_count() > 0) {
error(pos, "invalid start function: non-zero parameter count");
break;
}
break;
}
case WasmSection::Code::ImportTable: {
uint32_t import_table_count = consume_u32v("import table count");
module->import_table.reserve(SafeReserve(import_table_count));
// Decode import table.
for (uint32_t i = 0; i < import_table_count; ++i) {
if (failed()) break;
TRACE("DecodeImportTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module->import_table.push_back({nullptr, // sig
0, // sig_index
0, // module_name_offset
0, // module_name_length
0, // function_name_offset
0}); // function_name_length
WasmImport* import = &module->import_table.back();
import->sig_index = consume_sig_index(module, &import->sig);
const byte* pos = pc_;
import->module_name_offset =
consume_string(&import->module_name_length, true);
if (import->module_name_length == 0) {
error(pos, "import module name cannot be NULL");
}
import->function_name_offset =
consume_string(&import->function_name_length, true);
}
break;
}
case WasmSection::Code::ExportTable: {
// Declares an export table.
CheckForFunctions(module, section);
uint32_t export_table_count = consume_u32v("export table count");
module->export_table.reserve(SafeReserve(export_table_count));
// Decode export table.
for (uint32_t i = 0; i < export_table_count; ++i) {
if (failed()) break;
TRACE("DecodeExportTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module->export_table.push_back({0, // func_index
0, // name_offset
0}); // name_length
WasmExport* exp = &module->export_table.back();
WasmFunction* func;
exp->func_index = consume_func_index(module, &func);
exp->name_offset = consume_string(&exp->name_length, true);
}
// Check for duplicate exports.
if (ok() && module->export_table.size() > 1) {
std::vector<WasmExport> sorted_exports(module->export_table);
const byte* base = start_;
auto cmp_less = [base](const WasmExport& a, const WasmExport& b) {
// Return true if a < b.
uint32_t len = a.name_length;
if (len != b.name_length) return len < b.name_length;
return memcmp(base + a.name_offset, base + b.name_offset, len) <
0;
};
std::stable_sort(sorted_exports.begin(), sorted_exports.end(),
cmp_less);
auto it = sorted_exports.begin();
WasmExport* last = &*it++;
for (auto end = sorted_exports.end(); it != end; last = &*it++) {
DCHECK(!cmp_less(*it, *last)); // Vector must be sorted.
if (!cmp_less(*last, *it)) {
const byte* pc = start_ + it->name_offset;
error(pc, pc,
"Duplicate export name '%.*s' for functions %d and %d",
it->name_length, pc, last->func_index, it->func_index);
break;
}
}
}
break;
}
case WasmSection::Code::Max:
// Skip unknown sections.
TRACE("Unknown section: '");
for (uint32_t i = 0; i != string_length; ++i) {
TRACE("%c", *(section_name_start + i));
}
TRACE("'\n");
consume_bytes(section_length);
break;
}
if (pc_ != expected_section_end) {
const char* diff = pc_ < expected_section_end ? "shorter" : "longer";
size_t expected_length = static_cast<size_t>(section_length);
size_t actual_length = static_cast<size_t>(pc_ - section_start);
error(pc_, pc_,
"section \"%s\" %s (%zu bytes) than specified (%zu bytes)",
WasmSection::getName(section), diff, actual_length,
expected_length);
break;
}
}
done:
if (ok()) CalculateGlobalsOffsets(module);
const WasmModule* finished_module = module;
ModuleResult result = toResult(finished_module);
if (FLAG_dump_wasm_module) {
DumpModule(module, result);
}
return result;
}
uint32_t SafeReserve(uint32_t count) {
// Avoid OOM by only reserving up to a certain size.
const uint32_t kMaxReserve = 20000;
return count < kMaxReserve ? count : kMaxReserve;
}
void CheckForFunctions(WasmModule* module, WasmSection::Code section) {
if (module->functions.size() == 0) {
error(pc_ - 1, nullptr, "functions must appear before section %s",
WasmSection::getName(section));
}
}
int CheckSectionOrder(int current_order, WasmSection::Code section) {
int next_order = WasmSection::getOrder(section);
if (next_order == 0) return current_order;
if (next_order == current_order) {
error(pc_, pc_, "section \"%s\" already defined",
WasmSection::getName(section));
}
if (next_order < current_order) {
error(pc_, pc_, "section \"%s\" out of order",
WasmSection::getName(section));
}
return next_order;
}
// Decodes a single anonymous function starting at {start_}.
FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
WasmFunction* function) {
pc_ = start_;
function->sig = consume_sig(); // read signature
function->name_offset = 0; // ---- name
function->name_length = 0; // ---- name length
function->code_start_offset = off(pc_); // ---- code start
function->code_end_offset = off(limit_); // ---- code end
if (ok()) VerifyFunctionBody(0, module_env, function);
FunctionResult result;
result.CopyFrom(result_); // Copy error code and location.
result.val = function;
return result;
}
// Decodes a single function signature at {start}.
FunctionSig* DecodeFunctionSignature(const byte* start) {
pc_ = start;
FunctionSig* result = consume_sig();
return ok() ? result : nullptr;
}
private:
Zone* module_zone;
ModuleResult result_;
ModuleOrigin origin_;
uint32_t off(const byte* ptr) { return static_cast<uint32_t>(ptr - start_); }
// Decodes a single global entry inside a module starting at {pc_}.
void DecodeGlobalInModule(WasmGlobal* global) {
global->name_offset = consume_string(&global->name_length, false);
if (!unibrow::Utf8::Validate(start_ + global->name_offset,
global->name_length)) {
error("global name is not valid utf8");
}
global->type = mem_type();
global->offset = 0;
global->exported = consume_u8("exported") != 0;
}
bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) {
if (offset > limit) return false;
if ((offset + size) < offset) return false; // overflow
return (offset + size) <= limit;
}
// Decodes a single data segment entry inside a module starting at {pc_}.
void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) {
const byte* start = pc_;
segment->dest_addr = consume_u32v("destination");
segment->source_size = consume_u32v("source size");
segment->source_offset = static_cast<uint32_t>(pc_ - start_);
segment->init = true;
// Validate the data is in the module.
uint32_t module_limit = static_cast<uint32_t>(limit_ - start_);
if (!IsWithinLimit(module_limit, segment->source_offset,
segment->source_size)) {
error(start, "segment out of bounds of module");
}
// Validate that the segment will fit into the (minimum) memory.
uint32_t memory_limit =
WasmModule::kPageSize * (module ? module->min_mem_pages
: WasmModule::kMaxMemPages);
if (!IsWithinLimit(memory_limit, segment->dest_addr,
segment->source_size)) {
error(start, "segment out of bounds of memory");
}
consume_bytes(segment->source_size);
}
// Calculate individual global offsets and total size of globals table.
void CalculateGlobalsOffsets(WasmModule* module) {
uint32_t offset = 0;
if (module->globals.size() == 0) {
module->globals_size = 0;
return;
}
for (WasmGlobal& global : module->globals) {
byte size = WasmOpcodes::MemSize(global.type);
offset = (offset + size - 1) & ~(size - 1); // align
global.offset = offset;
offset += size;
}
module->globals_size = offset;
}
// Verifies the body (code) of a given function.
void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
WasmFunction* function) {
if (FLAG_trace_wasm_decoder || FLAG_trace_wasm_decode_time) {
OFStream os(stdout);
os << "Verifying WASM function " << WasmFunctionName(function, menv)
<< std::endl;
}
FunctionBody body = {menv, function->sig, start_,
start_ + function->code_start_offset,
start_ + function->code_end_offset};
TreeResult result = VerifyWasmCode(module_zone->allocator(), body);
if (result.failed()) {
// Wrap the error message from the function decoder.
std::ostringstream str;
str << "in function " << WasmFunctionName(function, menv) << ": ";
str << result;
std::string strval = str.str();
const char* raw = strval.c_str();
size_t len = strlen(raw);
char* buffer = new char[len];
strncpy(buffer, raw, len);
buffer[len - 1] = 0;
// Copy error code and location.
result_.CopyFrom(result);
result_.error_msg.Reset(buffer);
}
}
// Reads a single 32-bit unsigned integer interpreted as an offset, checking
// the offset is within bounds and advances.
uint32_t consume_offset(const char* name = nullptr) {
uint32_t offset = consume_u32(name ? name : "offset");
if (offset > static_cast<uint32_t>(limit_ - start_)) {
error(pc_ - sizeof(uint32_t), "offset out of bounds of module");
}
return offset;
}
// Reads a length-prefixed string, checking that it is within bounds. Returns
// the offset of the string, and the length as an out parameter.
uint32_t consume_string(uint32_t* length, bool validate_utf8) {
*length = consume_u32v("string length");
uint32_t offset = pc_offset();
TRACE(" +%u %-20s: (%u bytes)\n", offset, "string", *length);
if (validate_utf8 && !unibrow::Utf8::Validate(pc_, *length)) {
error(pc_, "no valid UTF-8 string");
}
consume_bytes(*length);
return offset;
}
uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) {
const byte* pos = pc_;
uint32_t sig_index = consume_u32v("signature index");
if (sig_index >= module->signatures.size()) {
error(pos, pos, "signature index %u out of bounds (%d signatures)",
sig_index, static_cast<int>(module->signatures.size()));
*sig = nullptr;
return 0;
}
*sig = module->signatures[sig_index];
return sig_index;
}
uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
const byte* pos = pc_;
uint32_t func_index = consume_u32v("function index");
if (func_index >= module->functions.size()) {
error(pos, pos, "function index %u out of bounds (%d functions)",
func_index, static_cast<int>(module->functions.size()));
*func = nullptr;
return 0;
}
*func = &module->functions[func_index];
return func_index;
}
// Reads a single 8-bit integer, interpreting it as a local type.
LocalType consume_local_type() {
byte val = consume_u8("local type");
LocalTypeCode t = static_cast<LocalTypeCode>(val);
switch (t) {
case kLocalVoid:
return kAstStmt;
case kLocalI32:
return kAstI32;
case kLocalI64:
return kAstI64;
case kLocalF32:
return kAstF32;
case kLocalF64:
return kAstF64;
default:
error(pc_ - 1, "invalid local type");
return kAstStmt;
}
}
// Reads a single 8-bit integer, interpreting it as a memory type.
MachineType mem_type() {
byte val = consume_u8("memory type");
MemTypeCode t = static_cast<MemTypeCode>(val);
switch (t) {
case kMemI8:
return MachineType::Int8();
case kMemU8:
return MachineType::Uint8();
case kMemI16:
return MachineType::Int16();
case kMemU16:
return MachineType::Uint16();
case kMemI32:
return MachineType::Int32();
case kMemU32:
return MachineType::Uint32();
case kMemI64:
return MachineType::Int64();
case kMemU64:
return MachineType::Uint64();
case kMemF32:
return MachineType::Float32();
case kMemF64:
return MachineType::Float64();
case kMemS128:
return MachineType::Simd128();
default:
error(pc_ - 1, "invalid memory type");
return MachineType::None();
}
}
// Parses a type entry, which is currently limited to functions only.
FunctionSig* consume_sig() {
const byte* pos = pc_;
byte form = consume_u8("type form");
if (form != kWasmFunctionTypeForm) {
error(pos, pos, "expected function type form (0x%02x), got: 0x%02x",
kWasmFunctionTypeForm, form);
return nullptr;
}
// parse parameter types
uint32_t param_count = consume_u32v("param count");
std::vector<LocalType> params;
for (uint32_t i = 0; i < param_count; ++i) {
LocalType param = consume_local_type();
if (param == kAstStmt) error(pc_ - 1, "invalid void parameter type");
params.push_back(param);
}
// parse return types
const byte* pt = pc_;
uint32_t return_count = consume_u32v("return count");
if (return_count > kMaxReturnCount) {
error(pt, pt, "return count of %u exceeds maximum of %u", return_count,
kMaxReturnCount);
return nullptr;
}
std::vector<LocalType> returns;
for (uint32_t i = 0; i < return_count; ++i) {
LocalType ret = consume_local_type();
if (ret == kAstStmt) error(pc_ - 1, "invalid void return type");
returns.push_back(ret);
}
// FunctionSig stores the return types first.
LocalType* buffer =
module_zone->NewArray<LocalType>(param_count + return_count);
uint32_t b = 0;
for (uint32_t i = 0; i < return_count; ++i) buffer[b++] = returns[i];
for (uint32_t i = 0; i < param_count; ++i) buffer[b++] = params[i];
return new (module_zone) FunctionSig(return_count, param_count, buffer);
}
};
// Helpers for nice error messages.
class ModuleError : public ModuleResult {
public:
explicit ModuleError(const char* msg) {
error_code = kError;
size_t len = strlen(msg) + 1;
char* result = new char[len];
strncpy(result, msg, len);
result[len - 1] = 0;
error_msg.Reset(result);
}
};
// Helpers for nice error messages.
class FunctionError : public FunctionResult {
public:
explicit FunctionError(const char* msg) {
error_code = kError;
size_t len = strlen(msg) + 1;
char* result = new char[len];
strncpy(result, msg, len);
result[len - 1] = 0;
error_msg.Reset(result);
}
};
Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
WasmSection::Code code) {
Decoder decoder(module_start, module_end);
uint32_t magic_word = decoder.consume_u32("wasm magic");
if (magic_word != kWasmMagic) decoder.error("wrong magic word");
uint32_t magic_version = decoder.consume_u32("wasm version");
if (magic_version != kWasmVersion) decoder.error("wrong wasm version");
while (decoder.more() && decoder.ok()) {
// Read the section name.
uint32_t string_length = decoder.consume_u32v("section name length");
const byte* section_name_start = decoder.pc();
decoder.consume_bytes(string_length);
if (decoder.failed()) break;
WasmSection::Code section =
WasmSection::lookup(section_name_start, string_length);
// Read and check the section size.
uint32_t section_length = decoder.consume_u32v("section length");
const byte* section_start = decoder.pc();
decoder.consume_bytes(section_length);
if (section == code && decoder.ok()) {
return Vector<const uint8_t>(section_start, section_length);
}
}
return Vector<const uint8_t>();
}
} // namespace
ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
const byte* module_start, const byte* module_end,
bool verify_functions, ModuleOrigin origin) {
size_t decode_memory_start = zone->allocation_size();
HistogramTimerScope wasm_decode_module_time_scope(
isolate->counters()->wasm_decode_module_time());
size_t size = module_end - module_start;
if (module_start > module_end) return ModuleError("start > end");
if (size >= kMaxModuleSize) return ModuleError("size > maximum module size");
// TODO(bradnelson): Improve histogram handling of size_t.
isolate->counters()->wasm_module_size_bytes()->AddSample(
static_cast<int>(size));
WasmModule* module = new WasmModule();
ModuleDecoder decoder(zone, module_start, module_end, origin);
ModuleResult result = decoder.DecodeModule(module, verify_functions);
// TODO(bradnelson): Improve histogram handling of size_t.
isolate->counters()->wasm_decode_module_peak_memory_bytes()->AddSample(
static_cast<int>(zone->allocation_size() - decode_memory_start));
return result;
}
FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start,
const byte* end) {
ModuleDecoder decoder(zone, start, end, kWasmOrigin);
return decoder.DecodeFunctionSignature(start);
}
FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
ModuleEnv* module_env,
const byte* function_start,
const byte* function_end) {
HistogramTimerScope wasm_decode_function_time_scope(
isolate->counters()->wasm_decode_function_time());
size_t size = function_end - function_start;
if (function_start > function_end) return FunctionError("start > end");
if (size > kMaxFunctionSize)
return FunctionError("size > maximum function size");
isolate->counters()->wasm_function_size_bytes()->AddSample(
static_cast<int>(size));
WasmFunction* function = new WasmFunction();
ModuleDecoder decoder(zone, function_start, function_end, kWasmOrigin);
return decoder.DecodeSingleFunction(module_env, function);
}
FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_end) {
Vector<const byte> code_section =
FindSection(module_start, module_end, WasmSection::Code::FunctionBodies);
Decoder decoder(code_section.start(), code_section.end());
if (!code_section.start()) decoder.error("no code section");
uint32_t functions_count = decoder.consume_u32v("functions count");
FunctionOffsets table;
// Take care of invalid input here.
if (functions_count < static_cast<unsigned>(code_section.length()) / 2)
table.reserve(functions_count);
int section_offset = static_cast<int>(code_section.start() - module_start);
DCHECK_LE(0, section_offset);
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
uint32_t size = decoder.consume_u32v("body size");
int offset = static_cast<int>(section_offset + decoder.pc_offset());
table.push_back(std::make_pair(offset, static_cast<int>(size)));
DCHECK(table.back().first >= 0 && table.back().second >= 0);
decoder.consume_bytes(size);
}
if (decoder.more()) decoder.error("unexpected additional bytes");
return decoder.toResult(std::move(table));
}
} // namespace wasm
} // namespace internal
} // namespace v8