[wasm] Refactor encoder.h to use a proper buffer and remove OldFunctions section.

This removes the last use of the old_functions section, which greatly
simplifies encoding.

R=bradnelson@chromium.org,aseemgarg@chromium.org,mtrofin@chromium.org

BUG=

Review-Url: https://codereview.chromium.org/2014533003
Cr-Commit-Position: refs/heads/master@{#36523}
This commit is contained in:
titzer 2016-05-25 09:12:09 -07:00 committed by Commit bot
parent 4a4f2537a3
commit 3412af0b40
15 changed files with 413 additions and 729 deletions

View File

@ -1759,12 +1759,14 @@ AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
// TODO(aseemgarg): probably should take zone (to write wasm to) as input so
// that zone in constructor may be thrown away once wasm module is written.
WasmModuleIndex* AsmWasmBuilder::Run(i::Handle<i::FixedArray>* foreign_args) {
ZoneBuffer* AsmWasmBuilder::Run(i::Handle<i::FixedArray>* foreign_args) {
AsmWasmBuilderImpl impl(isolate_, zone_, literal_, typer_);
impl.Compile();
*foreign_args = impl.GetForeignArgs();
ZoneBuffer* buffer = new (zone_) ZoneBuffer(zone_);
WasmModuleWriter* writer = impl.builder_->Build(zone_);
return writer->WriteTo(zone_);
writer->WriteTo(*buffer);
return buffer;
}
} // namespace wasm
} // namespace internal

View File

@ -22,7 +22,7 @@ class AsmWasmBuilder {
public:
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
AsmTyper* typer);
WasmModuleIndex* Run(Handle<FixedArray>* foreign_args);
ZoneBuffer* Run(Handle<FixedArray>* foreign_args);
private:
Isolate* isolate_;

View File

@ -30,70 +30,25 @@ namespace v8 {
namespace internal {
namespace wasm {
/*TODO: add error cases for adding too many locals, too many functions and bad
indices in body */
namespace {
void EmitUint8(byte** b, uint8_t x) {
Memory::uint8_at(*b) = x;
*b += 1;
}
void EmitUint16(byte** b, uint16_t x) {
WriteUnalignedUInt16(*b, x);
*b += 2;
}
void EmitUint32(byte** b, uint32_t x) {
WriteUnalignedUInt32(*b, x);
*b += 4;
}
void EmitVarInt(byte** b, size_t val) {
LEBHelper::write_u32v(b, static_cast<uint32_t>(val));
}
// Sections all start with a size, but it's unknown at the start.
// We generate a large varint which we then fixup later when the size is known.
//
// TODO(jfb) Not strictly necessary since sizes are calculated ahead of time.
const size_t kPaddedVarintSize = 5;
void FixupSection(byte* start, byte* end) {
// Same as LEBHelper::write_u32v, but fixed-width with zeroes in the MSBs.
size_t val = end - start - kPaddedVarintSize;
TRACE(" fixup %u\n", (unsigned)val);
for (size_t pos = 0; pos != kPaddedVarintSize; ++pos) {
size_t next = val >> 7;
byte out = static_cast<byte>(val & 0x7f);
if (pos != kPaddedVarintSize - 1) {
*(start++) = 0x80 | out;
val = next;
} else {
*(start++) = out;
// TODO(jfb) check that the pre-allocated fixup size isn't overflowed.
}
}
}
// Returns the start of the section, where the section VarInt size is.
byte* EmitSection(WasmSection::Code code, byte** b) {
// Emit a section name and the size as a padded varint that can be patched
// later.
size_t EmitSection(WasmSection::Code code, ZoneBuffer& buffer) {
// Emit the section name.
const char* name = WasmSection::getName(code);
TRACE("emit section: %s\n", name);
size_t length = WasmSection::getNameLength(code);
EmitVarInt(b, length); // Section name string size.
for (size_t i = 0; i != length; ++i) EmitUint8(b, name[i]);
buffer.write_size(length); // Section name string size.
buffer.write(reinterpret_cast<const byte*>(name), length);
// Emit a placeholder for the length.
byte* start = *b;
for (size_t padding = 0; padding != kPaddedVarintSize; ++padding) {
EmitUint8(b, 0xff); // Will get fixed up later.
}
return start;
return buffer.reserve_u32v();
}
// Patch the size of a section after it's finished.
void FixupSection(ZoneBuffer& buffer, size_t start) {
buffer.patch_u32v(start, static_cast<uint32_t>(buffer.offset() - start -
kPaddedVarInt32Size));
}
} // namespace
WasmFunctionBuilder::WasmFunctionBuilder(Zone* zone)
: locals_(zone), exported_(0), body_(zone), name_(zone) {}
@ -189,45 +144,30 @@ WasmFunctionEncoder::WasmFunctionEncoder(Zone* zone, LocalDeclEncoder locals,
bool exported)
: locals_(locals), exported_(exported), body_(zone), name_(zone) {}
uint32_t WasmFunctionEncoder::HeaderSize() const {
uint32_t size = 3;
size += 2;
if (HasName()) {
uint32_t name_size = NameSize();
size +=
static_cast<uint32_t>(LEBHelper::sizeof_u32v(name_size)) + name_size;
}
return size;
void WasmFunctionEncoder::WriteSignature(ZoneBuffer& buffer) const {
buffer.write_u32v(signature_index_);
}
uint32_t WasmFunctionEncoder::BodySize(void) const {
return static_cast<uint32_t>(body_.size() + locals_.Size());
}
uint32_t WasmFunctionEncoder::NameSize() const {
return HasName() ? static_cast<uint32_t>(name_.size()) : 0;
}
void WasmFunctionEncoder::Serialize(byte* buffer, byte** header,
byte** body) const {
uint8_t decl_bits = (exported_ ? kDeclFunctionExport : 0) |
(HasName() ? kDeclFunctionName : 0);
EmitUint8(header, decl_bits);
EmitUint16(header, signature_index_);
if (HasName()) {
EmitVarInt(header, NameSize());
for (size_t i = 0; i < name_.size(); ++i) {
EmitUint8(header, name_[i]);
void WasmFunctionEncoder::WriteExport(ZoneBuffer& buffer,
uint32_t func_index) const {
if (exported_) {
buffer.write_u32v(func_index);
buffer.write_size(name_.size());
if (name_.size() > 0) {
buffer.write(reinterpret_cast<const byte*>(&name_[0]), name_.size());
}
}
}
EmitUint16(header, static_cast<uint16_t>(body_.size() + locals_.Size()));
(*header) += locals_.Emit(*header);
void WasmFunctionEncoder::WriteBody(ZoneBuffer& buffer) const {
size_t locals_size = locals_.Size();
buffer.write_size(locals_size + body_.size());
buffer.EnsureSpace(locals_size);
byte** ptr = buffer.pos_ptr();
locals_.Emit(*ptr);
(*ptr) += locals_size; // UGLY: manual bump of position pointer
if (body_.size() > 0) {
std::memcpy(*header, &body_[0], body_.size());
(*header) += body_.size();
buffer.write(&body_[0], body_.size());
}
}
@ -239,22 +179,10 @@ WasmDataSegmentEncoder::WasmDataSegmentEncoder(Zone* zone, const byte* data,
}
}
uint32_t WasmDataSegmentEncoder::HeaderSize() const {
static const int kDataSegmentSize = 13;
return kDataSegmentSize;
}
uint32_t WasmDataSegmentEncoder::BodySize() const {
return static_cast<uint32_t>(data_.size());
}
void WasmDataSegmentEncoder::Serialize(byte* buffer, byte** header,
byte** body) const {
EmitVarInt(header, dest_);
EmitVarInt(header, static_cast<uint32_t>(data_.size()));
std::memcpy(*header, &data_[0], data_.size());
(*header) += data_.size();
void WasmDataSegmentEncoder::Write(ZoneBuffer& buffer) const {
buffer.write_u32v(dest_);
buffer.write_u32v(static_cast<uint32_t>(data_.size()));
buffer.write(&data_[0], data_.size());
}
WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
@ -365,223 +293,130 @@ WasmModuleWriter::WasmModuleWriter(Zone* zone)
indirect_functions_(zone),
globals_(zone) {}
struct Sizes {
size_t header_size;
size_t body_size;
void WasmModuleWriter::WriteTo(ZoneBuffer& buffer) const {
uint32_t exports = 0;
size_t total() { return header_size + body_size; }
void Add(size_t header, size_t body) {
header_size += header;
body_size += body;
}
void AddSection(WasmSection::Code code, size_t other_size) {
Add(kPaddedVarintSize +
LEBHelper::sizeof_u32v(WasmSection::getNameLength(code)) +
WasmSection::getNameLength(code),
0);
if (other_size) Add(LEBHelper::sizeof_u32v(other_size), 0);
}
};
WasmModuleIndex* WasmModuleWriter::WriteTo(Zone* zone) const {
Sizes sizes = {0, 0};
sizes.Add(2 * sizeof(uint32_t), 0); // header
if (globals_.size() > 0) {
sizes.AddSection(WasmSection::Code::Globals, globals_.size());
/* These globals never have names, so are always 3 bytes. */
sizes.Add(3 * globals_.size(), 0);
TRACE("Size after globals: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
if (signatures_.size() > 0) {
sizes.AddSection(WasmSection::Code::Signatures, signatures_.size());
for (auto sig : signatures_) {
sizes.Add(1 + LEBHelper::sizeof_u32v(sig->parameter_count()) +
sig->parameter_count() +
LEBHelper::sizeof_u32v(sig->return_count()) +
sig->return_count(),
0);
}
TRACE("Size after signatures: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
if (functions_.size() > 0) {
sizes.AddSection(WasmSection::Code::OldFunctions, functions_.size());
for (auto function : functions_) {
sizes.Add(function->HeaderSize() + function->BodySize(),
function->NameSize());
}
TRACE("Size after functions: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
if (imports_.size() > 0) {
sizes.AddSection(WasmSection::Code::ImportTable, imports_.size());
for (auto import : imports_) {
sizes.Add(LEBHelper::sizeof_u32v(import.sig_index), 0);
sizes.Add(LEBHelper::sizeof_u32v(import.name_length), 0);
sizes.Add(import.name_length, 0);
sizes.Add(1, 0);
}
TRACE("Size after imports: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
if (indirect_functions_.size() > 0) {
sizes.AddSection(WasmSection::Code::FunctionTable,
indirect_functions_.size());
for (auto function_index : indirect_functions_) {
sizes.Add(LEBHelper::sizeof_u32v(function_index), 0);
}
TRACE("Size after indirect functions: %u, %u\n",
(unsigned)sizes.header_size, (unsigned)sizes.body_size);
}
sizes.AddSection(WasmSection::Code::Memory, 0);
sizes.Add(kDeclMemorySize, 0);
TRACE("Size after memory: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
if (start_function_index_ >= 0) {
sizes.AddSection(WasmSection::Code::StartFunction, 0);
sizes.Add(LEBHelper::sizeof_u32v(start_function_index_), 0);
TRACE("Size after start: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
if (data_segments_.size() > 0) {
sizes.AddSection(WasmSection::Code::DataSegments, data_segments_.size());
for (auto segment : data_segments_) {
sizes.Add(segment->HeaderSize(), segment->BodySize());
}
TRACE("Size after data segments: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
if (sizes.body_size > 0) {
sizes.AddSection(WasmSection::Code::End, 0);
TRACE("Size after end: %u, %u\n", (unsigned)sizes.header_size,
(unsigned)sizes.body_size);
}
ZoneVector<uint8_t> buffer_vector(sizes.total(), zone);
byte* buffer = &buffer_vector[0];
byte* header = buffer;
byte* body = buffer + sizes.header_size;
// -- emit magic -------------------------------------------------------------
// == Emit magic =============================================================
TRACE("emit magic\n");
EmitUint32(&header, kWasmMagic);
EmitUint32(&header, kWasmVersion);
buffer.write_u32(kWasmMagic);
buffer.write_u32(kWasmVersion);
// -- emit globals -----------------------------------------------------------
if (globals_.size() > 0) {
byte* section = EmitSection(WasmSection::Code::Globals, &header);
EmitVarInt(&header, globals_.size());
for (auto global : globals_) {
EmitVarInt(&header, 0); // Length of the global name.
EmitUint8(&header, WasmOpcodes::MemTypeCodeFor(global.first));
EmitUint8(&header, global.second);
}
FixupSection(section, header);
}
// -- emit signatures --------------------------------------------------------
// == Emit signatures ========================================================
if (signatures_.size() > 0) {
byte* section = EmitSection(WasmSection::Code::Signatures, &header);
EmitVarInt(&header, signatures_.size());
size_t start = EmitSection(WasmSection::Code::Signatures, buffer);
buffer.write_size(signatures_.size());
for (FunctionSig* sig : signatures_) {
EmitUint8(&header, kWasmFunctionTypeForm);
EmitVarInt(&header, sig->parameter_count());
buffer.write_u8(kWasmFunctionTypeForm);
buffer.write_size(sig->parameter_count());
for (size_t j = 0; j < sig->parameter_count(); j++) {
EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j)));
buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j)));
}
EmitVarInt(&header, sig->return_count());
buffer.write_size(sig->return_count());
for (size_t j = 0; j < sig->return_count(); j++) {
EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetReturn(j)));
buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(sig->GetReturn(j)));
}
}
FixupSection(section, header);
FixupSection(buffer, start);
}
// -- emit imports -----------------------------------------------------------
// == Emit globals ===========================================================
if (globals_.size() > 0) {
size_t start = EmitSection(WasmSection::Code::Globals, buffer);
buffer.write_size(globals_.size());
for (auto global : globals_) {
buffer.write_u32v(0); // Length of the global name.
buffer.write_u8(WasmOpcodes::MemTypeCodeFor(global.first));
buffer.write_u8(global.second);
}
FixupSection(buffer, start);
}
// == Emit imports ===========================================================
if (imports_.size() > 0) {
byte* section = EmitSection(WasmSection::Code::ImportTable, &header);
EmitVarInt(&header, imports_.size());
size_t start = EmitSection(WasmSection::Code::ImportTable, buffer);
buffer.write_size(imports_.size());
for (auto import : imports_) {
EmitVarInt(&header, import.sig_index);
EmitVarInt(&header, import.name_length);
std::memcpy(header, import.name, import.name_length);
header += import.name_length;
EmitVarInt(&header, 0);
buffer.write_u32v(import.sig_index);
buffer.write_u32v(import.name_length);
buffer.write(reinterpret_cast<const byte*>(import.name),
import.name_length);
buffer.write_u32v(0);
}
FixupSection(section, header);
FixupSection(buffer, start);
}
// -- emit functions ---------------------------------------------------------
// == Emit function signatures ===============================================
if (functions_.size() > 0) {
byte* section = EmitSection(WasmSection::Code::OldFunctions, &header);
EmitVarInt(&header, functions_.size());
for (auto func : functions_) {
func->Serialize(buffer, &header, &body);
size_t start = EmitSection(WasmSection::Code::FunctionSignatures, buffer);
buffer.write_size(functions_.size());
for (auto function : functions_) {
function->WriteSignature(buffer);
if (function->exported()) exports++;
}
FixupSection(section, header);
FixupSection(buffer, start);
}
// -- emit function table ----------------------------------------------------
// == emit function table ====================================================
if (indirect_functions_.size() > 0) {
byte* section = EmitSection(WasmSection::Code::FunctionTable, &header);
EmitVarInt(&header, indirect_functions_.size());
size_t start = EmitSection(WasmSection::Code::FunctionTable, buffer);
buffer.write_size(indirect_functions_.size());
for (auto index : indirect_functions_) {
EmitVarInt(&header, index);
buffer.write_u32v(index);
}
FixupSection(section, header);
FixupSection(buffer, start);
}
// -- emit memory declaration ------------------------------------------------
// == emit memory declaration ================================================
{
byte* section = EmitSection(WasmSection::Code::Memory, &header);
EmitVarInt(&header, 16); // min memory size
EmitVarInt(&header, 16); // max memory size
EmitUint8(&header, 0); // memory export
size_t start = EmitSection(WasmSection::Code::Memory, buffer);
buffer.write_u32v(16); // min memory size
buffer.write_u32v(16); // max memory size
buffer.write_u8(0); // memory export
static_assert(kDeclMemorySize == 3, "memory size must match emit above");
FixupSection(section, header);
FixupSection(buffer, start);
}
// -- emit start function index ----------------------------------------------
// == emit exports ===========================================================
if (exports > 0) {
size_t start = EmitSection(WasmSection::Code::ExportTable, buffer);
buffer.write_u32v(exports);
uint32_t index = 0;
for (auto function : functions_) {
function->WriteExport(buffer, index++);
}
FixupSection(buffer, start);
}
// == emit start function index ==============================================
if (start_function_index_ >= 0) {
byte* section = EmitSection(WasmSection::Code::StartFunction, &header);
EmitVarInt(&header, start_function_index_);
FixupSection(section, header);
size_t start = EmitSection(WasmSection::Code::StartFunction, buffer);
buffer.write_u32v(start_function_index_);
FixupSection(buffer, start);
}
// -- emit data segments -----------------------------------------------------
// == emit code ==============================================================
if (functions_.size() > 0) {
size_t start = EmitSection(WasmSection::Code::FunctionBodies, buffer);
buffer.write_size(functions_.size());
for (auto function : functions_) {
function->WriteBody(buffer);
}
FixupSection(buffer, start);
}
// == emit data segments =====================================================
if (data_segments_.size() > 0) {
byte* section = EmitSection(WasmSection::Code::DataSegments, &header);
EmitVarInt(&header, data_segments_.size());
size_t start = EmitSection(WasmSection::Code::DataSegments, buffer);
buffer.write_size(data_segments_.size());
for (auto segment : data_segments_) {
segment->Serialize(buffer, &header, &body);
segment->Write(buffer);
}
FixupSection(section, header);
FixupSection(buffer, start);
}
if (sizes.body_size > 0) {
byte* section = EmitSection(WasmSection::Code::End, &header);
FixupSection(section, header);
}
return new (zone) WasmModuleIndex(buffer, buffer + sizes.total());
}
} // namespace wasm
} // namespace internal

View File

@ -10,6 +10,7 @@
#include "src/base/smart-pointers.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
@ -19,14 +20,104 @@ namespace v8 {
namespace internal {
namespace wasm {
class ZoneBuffer : public ZoneObject {
public:
static const uint32_t kInitialSize = 4096;
explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize)
: zone_(zone), buffer_(reinterpret_cast<byte*>(zone->New(initial))) {
pos_ = buffer_;
end_ = buffer_ + initial;
}
void write_u8(uint8_t x) {
EnsureSpace(1);
*(pos_++) = x;
}
void write_u16(uint16_t x) {
EnsureSpace(2);
WriteUnalignedUInt16(pos_, x);
pos_ += 2;
}
void write_u32(uint32_t x) {
EnsureSpace(4);
WriteUnalignedUInt32(pos_, x);
pos_ += 4;
}
void write_u32v(uint32_t val) {
EnsureSpace(kMaxVarInt32Size);
LEBHelper::write_u32v(&pos_, val);
}
void write_size(size_t val) {
EnsureSpace(kMaxVarInt32Size);
DCHECK_EQ(val, static_cast<uint32_t>(val));
LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val));
}
void write(const byte* data, size_t size) {
EnsureSpace(size);
memcpy(pos_, data, size);
pos_ += size;
}
size_t reserve_u32v() {
size_t off = offset();
EnsureSpace(kMaxVarInt32Size);
pos_ += kMaxVarInt32Size;
return off;
}
// Patch a (padded) u32v at the given offset to be the given value.
void patch_u32v(size_t offset, uint32_t val) {
byte* ptr = buffer_ + offset;
for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) {
uint32_t next = val >> 7;
byte out = static_cast<byte>(val & 0x7f);
if (pos != kPaddedVarInt32Size - 1) {
*(ptr++) = 0x80 | out;
val = next;
} else {
*(ptr++) = out;
}
}
}
size_t offset() { return static_cast<size_t>(pos_ - buffer_); }
size_t size() { return static_cast<size_t>(pos_ - buffer_); }
const byte* begin() { return buffer_; }
const byte* end() { return pos_; }
void EnsureSpace(size_t size) {
if ((pos_ + size) > end_) {
size_t new_size = 4096 + (end_ - buffer_) * 3;
byte* new_buffer = reinterpret_cast<byte*>(zone_->New(new_size));
memcpy(new_buffer, buffer_, (pos_ - buffer_));
pos_ = new_buffer + (pos_ - buffer_);
buffer_ = new_buffer;
end_ = new_buffer + new_size;
}
}
byte** pos_ptr() { return &pos_; }
private:
Zone* zone_;
byte* buffer_;
byte* pos_;
byte* end_;
};
class WasmModuleBuilder;
class WasmFunctionEncoder : public ZoneObject {
public:
uint32_t HeaderSize() const;
uint32_t BodySize() const;
uint32_t NameSize() const;
void Serialize(byte* buffer, byte** header, byte** body) const;
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const;
void WriteBody(ZoneBuffer& buffer) const;
bool exported() const { return exported_; }
private:
WasmFunctionEncoder(Zone* zone, LocalDeclEncoder locals, bool exported);
@ -71,28 +162,13 @@ class WasmDataSegmentEncoder : public ZoneObject {
public:
WasmDataSegmentEncoder(Zone* zone, const byte* data, uint32_t size,
uint32_t dest);
uint32_t HeaderSize() const;
uint32_t BodySize() const;
void Serialize(byte* buffer, byte** header, byte** body) const;
void Write(ZoneBuffer& buffer) const;
private:
ZoneVector<byte> data_;
uint32_t dest_;
};
class WasmModuleIndex : public ZoneObject {
public:
const byte* Begin() const { return begin_; }
const byte* End() const { return end_; }
private:
friend class WasmModuleWriter;
WasmModuleIndex(const byte* begin, const byte* end)
: begin_(begin), end_(end) {}
const byte* begin_;
const byte* end_;
};
struct WasmFunctionImport {
uint32_t sig_index;
const char* name;
@ -101,7 +177,7 @@ struct WasmFunctionImport {
class WasmModuleWriter : public ZoneObject {
public:
WasmModuleIndex* WriteTo(Zone* zone) const;
void WriteTo(ZoneBuffer& buffer) const;
private:
friend class WasmModuleBuilder;

View File

@ -9,6 +9,9 @@ namespace v8 {
namespace internal {
namespace wasm {
static const size_t kPaddedVarInt32Size = 5;
static const size_t kMaxVarInt32Size = 5;
class LEBHelper {
public:
// Write a 32-bit unsigned LEB to {dest}, updating {dest} to point after

View File

@ -118,6 +118,10 @@ class ModuleDecoder : public Decoder {
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);
@ -171,8 +175,7 @@ class ModuleDecoder : public Decoder {
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false}); // exported
0}); // code_end_offset
WasmFunction* function = &module->functions.back();
function->sig_index = consume_sig_index(module, &function->sig);
}
@ -204,44 +207,6 @@ class ModuleDecoder : public Decoder {
}
break;
}
case WasmSection::Code::OldFunctions: {
int length;
uint32_t functions_count = consume_u32v(&length, "functions count");
module->functions.reserve(SafeReserve(functions_count));
// Set up module environment for verification.
ModuleEnv menv;
menv.module = module;
menv.instance = nullptr;
menv.origin = origin_;
// Decode functions.
for (uint32_t i = 0; i < functions_count; i++) {
if (failed()) break;
TRACE("DecodeFunction[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
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
false}); // exported
WasmFunction* function = &module->functions.back();
DecodeFunctionInModule(module, function, false);
}
if (ok() && verify_functions) {
for (uint32_t i = 0; i < functions_count; i++) {
if (failed()) break;
WasmFunction* function = &module->functions[i];
VerifyFunctionBody(i, &menv, function);
if (result_.failed()) {
error(result_.error_pc, result_.error_msg.get());
}
}
}
break;
}
case WasmSection::Code::Names: {
int length;
const byte* pos = pc_;
@ -464,7 +429,6 @@ class ModuleDecoder : public Decoder {
function->name_length = 0; // ---- name length
function->code_start_offset = off(pc_); // ---- code start
function->code_end_offset = off(limit_); // ---- code end
function->exported = false; // ---- exported
if (ok()) VerifyFunctionBody(0, module_env, function);
@ -498,46 +462,6 @@ class ModuleDecoder : public Decoder {
global->exported = consume_u8("exported") != 0;
}
// Decodes a single function entry inside a module starting at {pc_}.
// TODO(titzer): legacy function body; remove
void DecodeFunctionInModule(WasmModule* module, WasmFunction* function,
bool verify_body = true) {
byte decl_bits = consume_u8("function decl");
const byte* sigpos = pc_;
function->sig_index = consume_u16("signature index");
if (function->sig_index >= module->signatures.size()) {
return error(sigpos, "invalid signature index");
} else {
function->sig = module->signatures[function->sig_index];
}
TRACE(" +%d <function attributes:%s%s>\n", static_cast<int>(pc_ - start_),
decl_bits & kDeclFunctionName ? " name" : "",
decl_bits & kDeclFunctionExport ? " exported" : "");
function->exported = decl_bits & kDeclFunctionExport;
if (decl_bits & kDeclFunctionName) {
function->name_offset =
consume_string(&function->name_length, function->exported);
}
uint16_t size = consume_u16("body size");
if (ok()) {
if ((pc_ + size) > limit_) {
return error(pc_, limit_,
"expected %d bytes for function body, fell off end", size);
}
function->code_start_offset = static_cast<uint32_t>(pc_ - start_);
function->code_end_offset = function->code_start_offset + size;
TRACE(" +%d %-20s: (%d bytes)\n", static_cast<int>(pc_ - start_),
"function body", size);
pc_ += size;
}
}
bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) {
if (offset > limit) return false;
if ((offset + size) < offset) return false; // overflow

View File

@ -121,7 +121,7 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (result.val) delete result.val;
}
v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
v8::internal::wasm::ZoneBuffer* TranslateAsmModule(
i::ParseInfo* info, ErrorThrower* thrower,
i::Handle<i::FixedArray>* foreign_args) {
info->set_global();
@ -154,9 +154,7 @@ v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
info->literal(), &typer);
auto module = builder.Run(foreign_args);
return module;
return builder.Run(foreign_args);
}
i::MaybeHandle<i::JSObject> InstantiateModuleCommon(
@ -231,7 +229,7 @@ void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
i::MaybeHandle<i::Object> maybe_module_object =
InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower,
InstantiateModuleCommon(args, module->begin(), module->end(), &thrower,
internal::wasm::kAsmJsOrigin);
if (maybe_module_object.is_null()) {
return;

View File

@ -577,8 +577,6 @@ bool FinishCompilation(Isolate* isolate, const WasmModule* module,
DCHECK_EQ(i, func.func_index);
WasmName str = module->GetName(func.name_offset, func.name_length);
Handle<Code> code = Handle<Code>::null();
Handle<JSFunction> function = Handle<JSFunction>::null();
Handle<String> function_name = Handle<String>::null();
if (FLAG_wasm_num_compilation_tasks != 0) {
code = results[i];
} else {
@ -591,28 +589,12 @@ bool FinishCompilation(Isolate* isolate, const WasmModule* module,
str.start());
return false;
}
if (func.exported) {
function_name = factory->InternalizeUtf8String(str);
function = compiler::CompileJSToWasmWrapper(
isolate, &module_env, function_name, code, instance.js_object, i);
code_stats.Record(function->code());
}
if (!code.is_null()) {
// Install the code into the linker table.
module_env.linker->Finish(i, code);
code_table->set(i, *code);
code_stats.Record(*code);
}
if (func.exported) {
// Exported functions are installed as read-only properties on the
// module.
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, instance.js_object, function_name, &desc,
Object::THROW_ON_ERROR);
if (!status.IsJust())
thrower.Error("export of %.*s failed.", str.length(), str.start());
}
}
return true;
}
@ -786,14 +768,19 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
// Create and populate the exports object.
//-------------------------------------------------------------------------
if (export_table.size() > 0 || mem_export) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate->native_context()->object_function(), isolate);
Handle<JSObject> exports_object =
factory->NewJSObject(object_function, TENURED);
Handle<String> exports_name = factory->InternalizeUtf8String("exports");
JSObject::AddProperty(instance.js_object, exports_name, exports_object,
READ_ONLY);
Handle<JSObject> exports_object;
if (origin == kWasmOrigin) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate->native_context()->object_function(), isolate);
exports_object = factory->NewJSObject(object_function, TENURED);
Handle<String> exports_name = factory->InternalizeUtf8String("exports");
JSObject::AddProperty(instance.js_object, exports_name, exports_object,
READ_ONLY);
} else {
// Just export the functions directly on the object returned.
exports_object = instance.js_object;
}
// Compile wrappers and add them to the exports object.
for (const WasmExport& exp : export_table) {
@ -808,8 +795,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
if (!status.IsJust())
if (!status.IsJust()) {
thrower.Error("export of %.*s failed.", str.length(), str.start());
break;
}
}
if (mem_export) {
@ -879,8 +868,9 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
Zone zone(isolate->allocator());
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
ModuleResult result = DecodeWasmModule(isolate, &zone, module_start,
module_end, false, kWasmOrigin);
ModuleResult result =
DecodeWasmModule(isolate, &zone, module_start, module_end, false,
asm_js ? kAsmJsOrigin : kWasmOrigin);
if (result.failed()) {
if (result.val) {
delete result.val;
@ -924,34 +914,25 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) {
module_env.linker = &linker;
module_env.origin = module->origin;
// Compile all functions.
Handle<Code> main_code = Handle<Code>::null(); // record last code.
uint32_t index = 0;
int main_index = 0;
for (const WasmFunction& func : module->functions) {
DCHECK_EQ(index, func.func_index);
// Compile the function and install it in the code table.
Handle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction(
&thrower, isolate, &module_env, &func);
if (!code.is_null()) {
if (func.exported) {
main_code = code;
main_index = index;
}
linker.Finish(index, code);
}
if (thrower.error()) return -1;
index++;
if (module->export_table.size() == 0) {
thrower.Error("WASM.compileRun() failed: no exported functions");
return -2;
}
if (main_code.is_null()) {
thrower.Error("WASM.compileRun() failed: no main code found");
return -1;
// Compile all functions.
for (const WasmFunction& func : module->functions) {
// Compile the function and install it in the linker.
Handle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction(
&thrower, isolate, &module_env, &func);
if (!code.is_null()) linker.Finish(func.func_index, code);
if (thrower.error()) return -1;
}
linker.Link(instance.function_table, instance.module->function_table);
// Wrap the main code so it can be called as a JS function.
uint32_t main_index = module->export_table.back().func_index;
Handle<Code> main_code = linker.GetFunctionCode(main_index);
Handle<String> name = isolate->factory()->NewStringFromStaticChars("main");
Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(

View File

@ -42,15 +42,12 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
F(FunctionBodies, 8, "code") \
F(DataSegments, 9, "data") \
F(Names, 10, "name") \
F(OldFunctions, 0, "old_function") \
F(Globals, 0, "global") \
F(End, 0, "end")
// Contants for the above section types: {LEB128 length, characters...}.
#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y'
#define WASM_SECTION_SIGNATURES 4, 't', 'y', 'p', 'e'
#define WASM_SECTION_OLD_FUNCTIONS \
12, 'o', 'l', 'd', '_', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
#define WASM_SECTION_GLOBALS 6, 'g', 'l', 'o', 'b', 'a', 'l'
#define WASM_SECTION_DATA_SEGMENTS 4, 'd', 'a', 't', 'a'
#define WASM_SECTION_FUNCTION_TABLE 5, 't', 'a', 'b', 'l', 'e'
@ -66,7 +63,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
// Constants for the above section headers' size (LEB128 + characters).
#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
#define WASM_SECTION_SIGNATURES_SIZE ((size_t)5)
#define WASM_SECTION_OLD_FUNCTIONS_SIZE ((size_t)13)
#define WASM_SECTION_GLOBALS_SIZE ((size_t)7)
#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)5)
#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)6)
@ -114,7 +110,6 @@ struct WasmFunction {
uint32_t name_length; // length in bytes of the name.
uint32_t code_start_offset; // offset in the module bytes of code start.
uint32_t code_end_offset; // offset in the module bytes of code end.
bool exported; // true if this function is exported.
};
// Static representation of an imported WASM function.

View File

@ -29,7 +29,8 @@ std::ostream& operator<<(std::ostream& os, const ErrorCode& error_code) {
}
void ErrorThrower::Error(const char* format, ...) {
if (error_) return; // only report the first error.
// only report the first error.
if (error_ || isolate_->has_pending_exception()) return;
error_ = true;
char buffer[256];

View File

@ -20,12 +20,17 @@ using namespace v8::internal::compiler;
using namespace v8::internal::wasm;
namespace {
void TestModule(WasmModuleIndex* module, int32_t expected_result) {
void TestModule(Zone* zone, WasmModuleBuilder* builder,
int32_t expected_result) {
ZoneBuffer buffer(zone);
WasmModuleWriter* writer = builder->Build(zone);
writer->WriteTo(buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
WasmJs::InstallWasmFunctionMap(isolate, isolate->native_context());
int32_t result =
CompileAndRunWasmModule(isolate, module->Begin(), module->End());
CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
CHECK_EQ(expected_result, result);
}
} // namespace
@ -43,8 +48,7 @@ TEST(Run_WasmModule_Return114) {
f->Exported(1);
byte code[] = {WASM_I8(kReturnValue)};
f->EmitCode(code, sizeof(code));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), kReturnValue);
TestModule(&zone, builder, kReturnValue);
}
TEST(Run_WasmModule_CallAdd) {
@ -69,8 +73,7 @@ TEST(Run_WasmModule_CallAdd) {
f->Exported(1);
byte code2[] = {WASM_CALL_FUNCTION2(f1_index, WASM_I8(77), WASM_I8(22))};
f->EmitCode(code2, sizeof(code2));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 99);
TestModule(&zone, builder, 99);
}
TEST(Run_WasmModule_ReadLoadedDataSegment) {
@ -91,8 +94,7 @@ TEST(Run_WasmModule_ReadLoadedDataSegment) {
byte data[] = {0xaa, 0xbb, 0xcc, 0xdd};
builder->AddDataSegment(new (&zone) WasmDataSegmentEncoder(
&zone, data, sizeof(data), kDataSegmentDest0));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 0xddccbbaa);
TestModule(&zone, builder, 0xddccbbaa);
}
TEST(Run_WasmModule_CheckMemoryIsZero) {
@ -117,8 +119,7 @@ TEST(Run_WasmModule_CheckMemoryIsZero) {
WASM_BRV(2, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))),
WASM_I8(11))};
f->EmitCode(code, sizeof(code));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 11);
TestModule(&zone, builder, 11);
}
TEST(Run_WasmModule_CallMain_recursive) {
@ -142,8 +143,7 @@ TEST(Run_WasmModule_CallMain_recursive) {
WASM_BRV(1, WASM_CALL_FUNCTION0(0))),
WASM_BRV(0, WASM_I8(55))))};
f->EmitCode(code, sizeof(code));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 55);
TestModule(&zone, builder, 55);
}
TEST(Run_WasmModule_Global) {
@ -168,6 +168,5 @@ TEST(Run_WasmModule_Global) {
WASM_STORE_GLOBAL(global2, WASM_I32V_1(41)),
WASM_RETURN1(WASM_CALL_FUNCTION0(f1_index))};
f->EmitCode(code2, sizeof(code2));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 97);
TestModule(&zone, builder, 97);
}

View File

@ -41,9 +41,9 @@ void testFunctionNameTable(Vector<Vector<const char>> names) {
name.start() + name.length());
// Make every second function name null-terminated.
if (func_index % 2) all_names.push_back('\0');
module.functions.push_back(
{nullptr, 0, 0, static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0, false});
module.functions.push_back({nullptr, 0, 0,
static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0});
++func_index;
}

View File

@ -179,7 +179,7 @@ class TestingModule : public ModuleEnv {
module_.functions.reserve(kMaxFunctions);
}
uint32_t index = static_cast<uint32_t>(module->functions.size());
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false});
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0});
instance->function_code.push_back(code);
if (interpreter_) {
const WasmFunction* function = &module->functions.back();

View File

@ -1169,14 +1169,13 @@ class TestModuleEnv : public ModuleEnv {
return static_cast<byte>(mod.signatures.size() - 1);
}
byte AddFunction(FunctionSig* sig) {
mod.functions.push_back({sig, // sig
0, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false}); // exported
mod.functions.push_back({sig, // sig
0, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0}); // code_end_offset
CHECK(mod.functions.size() <= 127);
return static_cast<byte>(mod.functions.size() - 1);
}

View File

@ -39,6 +39,38 @@ namespace wasm {
#define EMPTY_FUNCTION_BODIES_SECTION SECTION(FUNCTION_BODIES, 1), 0
#define EMPTY_NAMES_SECTION SECTION(NAMES, 1), 0
#define X1(...) __VA_ARGS__
#define X2(...) __VA_ARGS__, __VA_ARGS__
#define X3(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__
#define X4(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__
#define ONE_EMPTY_FUNCTION WASM_SECTION_FUNCTION_SIGNATURES, 1 + 1 * 1, 1, X1(0)
#define TWO_EMPTY_FUNCTIONS \
WASM_SECTION_FUNCTION_SIGNATURES, 1 + 2 * 1, 2, X2(0)
#define THREE_EMPTY_FUNCTIONS \
WASM_SECTION_FUNCTION_SIGNATURES, 1 + 3 * 1, 3, X3(0)
#define FOUR_EMPTY_FUNCTIONS \
WASM_SECTION_FUNCTION_SIGNATURES, 1 + 4 * 1, 4, X4(0)
#define ONE_EMPTY_BODY \
WASM_SECTION_FUNCTION_BODIES, 1 + 1 * (1 + SIZEOF_EMPTY_BODY), 1, \
X1(SIZEOF_EMPTY_BODY, EMPTY_BODY)
#define TWO_EMPTY_BODIES \
WASM_SECTION_FUNCTION_BODIES, 1 + 2 * (1 + SIZEOF_EMPTY_BODY), 2, \
X2(SIZEOF_EMPTY_BODY, EMPTY_BODY)
#define THREE_EMPTY_BODIES \
WASM_SECTION_FUNCTION_BODIES, 1 + 3 * (1 + SIZEOF_EMPTY_BODY), 3, \
X3(SIZEOF_EMPTY_BODY, EMPTY_BODY)
#define FOUR_EMPTY_BODIES \
WASM_SECTION_FUNCTION_BODIES, 1 + 4 * (1 + SIZEOF_EMPTY_BODY), 4, \
X4(SIZEOF_EMPTY_BODY, EMPTY_BODY)
#define SIGNATURES_SECTION_VOID_VOID \
SECTION(SIGNATURES, 1 + SIZEOF_SIG_ENTRY_v_v), 1, SIG_ENTRY_v_v
@ -321,163 +353,6 @@ TEST_F(WasmModuleVerifyTest, MultipleSignatures) {
EXPECT_OFF_END_FAILURE(data, 1, sizeof(data));
}
TEST_F(WasmModuleVerifyTest, FunctionWithoutSig) {
static const byte data[] = {
SECTION(OLD_FUNCTIONS, 25), 1,
// func#0 ------------------------------------------------------
SIG_INDEX(0), // signature index
NO_NAME, // name length
U32_LE(0), // code start offset
U32_LE(0), // code end offset
U16_LE(899), // local int32 count
U16_LE(799), // local int64 count
U16_LE(699), // local float32 count
U16_LE(599), // local float64 count
0, // exported
0 // external
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_FALSE(result.ok());
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, OneEmptyVoidVoidFunction) {
const int kCodeStartOffset = 41;
const int kCodeEndOffset = kCodeStartOffset + 1;
static const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
// func#0 ------------------------------------------------------
SECTION(OLD_FUNCTIONS, 10), 1, kDeclFunctionExport | kDeclFunctionName,
SIG_INDEX(0), // signature index
NAME_LENGTH(2), 'h', 'i', // name
1, 0, // size
kExprNop,
};
{
// Should decode to exactly one function.
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(0, result.val->globals.size());
EXPECT_EQ(1, result.val->signatures.size());
EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(0, result.val->data_segments.size());
EXPECT_EQ(0, result.val->function_table.size());
const WasmFunction* function = &result.val->functions.back();
EXPECT_EQ(37, function->name_offset);
EXPECT_EQ(2, function->name_length);
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
EXPECT_TRUE(function->exported);
if (result.val) delete result.val;
}
EXPECT_OFF_END_FAILURE(data, 16, sizeof(data));
}
TEST_F(WasmModuleVerifyTest, OneFunctionWithNopBody) {
static const byte kCodeStartOffset = 38;
static const byte kCodeEndOffset = kCodeStartOffset + 1;
static const byte data[] = {
SIGNATURES_SECTION_VOID_VOID, // --
SECTION(OLD_FUNCTIONS, 7), 1,
// func#0 ------------------------------------------------------
0, // no name, no locals
0, 0, // signature index
1, 0, // body size
kExprNop // body
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(1, result.val->functions.size());
const WasmFunction* function = &result.val->functions.back();
EXPECT_EQ(0, function->name_length);
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
EXPECT_FALSE(function->exported);
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, OneGlobalOneFunctionWithNopBodyOneDataSegment) {
static const byte kNameOffset = 49;
static const byte kCodeStartOffset = 53;
static const byte kCodeEndOffset = kCodeStartOffset + 3;
static const byte kDataSegmentSourceOffset = kCodeEndOffset + 22;
static const byte data[] = {
// global#0 --------------------------------------------------
SECTION(GLOBALS, 4), 1,
0, // name length
kMemU8, // memory type
0, // exported
// sig#0 -----------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// func#0 ----------------------------------------------------
SECTION(OLD_FUNCTIONS, 12), 1,
kDeclFunctionName, // --
SIG_INDEX(0), // signature index
2, 'h', 'i', // name
3, 0, // body size
kExprNop, // func#0 body
kExprNop, // func#0 body
kExprNop, // func#0 body
// memory section --------------------------------------------
SECTION(MEMORY, 3), 28, 28, 1,
// segment#0 -------------------------------------------------
SECTION(DATA_SEGMENTS, 10), 1,
U32V_3(0x8b3ae), // dest addr
U32V_1(5), // source size
0, 1, 2, 3, 4, // data bytes
// rest ------------------------------------------------------
SECTION(END, 0),
};
{
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(1, result.val->globals.size());
EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(1, result.val->data_segments.size());
const WasmGlobal* global = &result.val->globals.back();
EXPECT_EQ(0, global->name_length);
EXPECT_EQ(MachineType::Uint8(), global->type);
EXPECT_EQ(0, global->offset);
EXPECT_FALSE(global->exported);
const WasmFunction* function = &result.val->functions.back();
EXPECT_EQ(kNameOffset, function->name_offset);
EXPECT_EQ(2, function->name_length);
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
EXPECT_FALSE(function->exported);
const WasmDataSegment* segment = &result.val->data_segments.back();
EXPECT_EQ(0x8b3ae, segment->dest_addr);
EXPECT_EQ(kDataSegmentSourceOffset, segment->source_offset);
EXPECT_EQ(5, segment->source_size);
EXPECT_TRUE(segment->init);
if (result.val) delete result.val;
}
}
TEST_F(WasmModuleVerifyTest, OneDataSegment) {
const byte kDataSegmentSourceOffset = 30;
const byte data[] = {
@ -605,9 +480,8 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
static const byte data[] = {
// sig#0 -------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// func#0 ------------------------------------------------------
SECTION(OLD_FUNCTIONS, 1 + SIZEOF_EMPTY_FUNCTION), 1, // --
EMPTY_FUNCTION(0),
// funcs ------------------------------------------------------
ONE_EMPTY_FUNCTION,
// indirect table ----------------------------------------------
SECTION(FUNCTION_TABLE, 2), 1, U32V_1(0)};
@ -629,12 +503,8 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
2, // --
SIG_ENTRY_v_v, // void -> void
SIG_ENTRY_v_x(kLocalI32), // void -> i32
// func#0 ------------------------------------------------------
SECTION(OLD_FUNCTIONS, 1 + 4 * SIZEOF_EMPTY_FUNCTION), 4, // --
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(1), // --
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(1), // --
// funcs ------------------------------------------------------
FOUR_EMPTY_FUNCTIONS,
// indirect table ----------------------------------------------
SECTION(FUNCTION_TABLE, 9), 8,
U32V_1(0), // --
@ -645,7 +515,7 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
U32V_1(1), // --
U32V_1(2), // --
U32V_1(3), // --
};
FOUR_EMPTY_BODIES};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
@ -676,8 +546,7 @@ TEST_F(WasmModuleVerifyTest, IndirectFunctionInvalidIndex) {
// sig#0 -------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// functions ---------------------------------------------------
SECTION(OLD_FUNCTIONS, 1 + SIZEOF_EMPTY_FUNCTION), 1, // --
EMPTY_FUNCTION(0),
ONE_EMPTY_FUNCTION,
// indirect table ----------------------------------------------
SECTION(FUNCTION_TABLE, 3), 1, 1, 0,
};
@ -839,7 +708,6 @@ TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) {
EXPECT_EQ(SIZEOF_SIG_ENTRY_v_v, function->code_start_offset);
EXPECT_EQ(arraysize(data), function->code_end_offset);
// TODO(titzer): verify encoding of local declarations
EXPECT_FALSE(function->exported);
}
if (result.val) delete result.val;
@ -1071,22 +939,24 @@ TEST_F(WasmModuleVerifyTest, ImportTable_off_end) {
}
TEST_F(WasmModuleVerifyTest, ExportTable_empty1) {
static const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
SECTION(OLD_FUNCTIONS, 1 + SIZEOF_EMPTY_FUNCTION),
1,
EMPTY_FUNCTION(0),
SECTION(EXPORT_TABLE, 1),
0 // --
};
EXPECT_VERIFIES(data);
static const byte data[] = {// signatures
SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION,
SECTION(EXPORT_TABLE, 1),
0, // --
ONE_EMPTY_BODY};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(0, result.val->export_table.size());
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, ExportTable_empty2) {
static const byte data[] = {
SECTION(SIGNATURES, 1), 0, SECTION(OLD_FUNCTIONS, 1), 0,
SECTION(EXPORT_TABLE, 1), 0 // --
SECTION(SIGNATURES, 1), 0, SECTION(EXPORT_TABLE, 1), 0 // --
};
// TODO(titzer): current behavior treats empty functions section as missing.
EXPECT_FAILURE(data);
@ -1105,85 +975,88 @@ TEST_F(WasmModuleVerifyTest, ExportTable_NoFunctions2) {
}
TEST_F(WasmModuleVerifyTest, ExportTableOne) {
static const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
SECTION(OLD_FUNCTIONS, 1 + SIZEOF_EMPTY_FUNCTION),
1, // functions
EMPTY_FUNCTION(0), // --
SECTION(EXPORT_TABLE, 3),
1, // exports
FUNC_INDEX(0), // --
NO_NAME // --
};
EXPECT_VERIFIES(data);
static const byte data[] = {// signatures
SIGNATURES_SECTION_VOID_VOID,
ONE_EMPTY_FUNCTION,
SECTION(EXPORT_TABLE, 3),
1, // exports
FUNC_INDEX(0), // --
NO_NAME, // --
ONE_EMPTY_BODY};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(1, result.val->export_table.size());
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, ExportTableTwo) {
static const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
SECTION(OLD_FUNCTIONS, 1 + SIZEOF_EMPTY_FUNCTION),
1, // functions
EMPTY_FUNCTION(0), // --
SECTION(EXPORT_TABLE, 12),
2, // exports
FUNC_INDEX(0), // --
NAME_LENGTH(4),
'n',
'a',
'm',
'e', // --
FUNC_INDEX(0), // --
NAME_LENGTH(3),
'n',
'o',
'm' // --
};
EXPECT_VERIFIES(data);
static const byte data[] = {// signatures
SIGNATURES_SECTION_VOID_VOID,
ONE_EMPTY_FUNCTION,
SECTION(EXPORT_TABLE, 12),
2, // exports
FUNC_INDEX(0), // --
NAME_LENGTH(4),
'n',
'a',
'm',
'e', // --
FUNC_INDEX(0), // --
NAME_LENGTH(3),
'n',
'o',
'm', // --
ONE_EMPTY_BODY};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(2, result.val->export_table.size());
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, ExportTableThree) {
static const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
SECTION(OLD_FUNCTIONS, 1 + 3 * SIZEOF_EMPTY_FUNCTION),
3, // functions
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
SECTION(EXPORT_TABLE, 10),
3, // exports
FUNC_INDEX(0), // --
NAME_LENGTH(1),
'a', // --
FUNC_INDEX(1), // --
NAME_LENGTH(1),
'b', // --
FUNC_INDEX(2), // --
NAME_LENGTH(1),
'c' // --
};
EXPECT_VERIFIES(data);
static const byte data[] = {// signatures
SIGNATURES_SECTION_VOID_VOID,
THREE_EMPTY_FUNCTIONS,
SECTION(EXPORT_TABLE, 10),
3, // exports
FUNC_INDEX(0), // --
NAME_LENGTH(1),
'a', // --
FUNC_INDEX(1), // --
NAME_LENGTH(1),
'b', // --
FUNC_INDEX(2), // --
NAME_LENGTH(1),
'c', // --
THREE_EMPTY_BODIES};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_OK(result);
EXPECT_EQ(3, result.val->functions.size());
EXPECT_EQ(3, result.val->export_table.size());
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, ExportTableThreeOne) {
for (int i = 0; i < 6; i++) {
const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
SECTION(OLD_FUNCTIONS, 1 + 3 * SIZEOF_EMPTY_FUNCTION),
3, // functions
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
SECTION(EXPORT_TABLE, 5),
1, // exports
FUNC_INDEX(i), // --
NAME_LENGTH(2),
'e',
'x', // --
};
const byte data[] = {// signatures
SIGNATURES_SECTION_VOID_VOID,
THREE_EMPTY_FUNCTIONS,
SECTION(EXPORT_TABLE, 5),
1, // exports
FUNC_INDEX(i), // --
NAME_LENGTH(2),
'e',
'x', // --
THREE_EMPTY_BODIES};
if (i < 3) {
EXPECT_VERIFIES(data);
@ -1197,9 +1070,7 @@ TEST_F(WasmModuleVerifyTest, ExportTableOne_off_end) {
static const byte data[] = {
// signatures
SIGNATURES_SECTION_VOID_VOID,
SECTION(OLD_FUNCTIONS, 1 + SIZEOF_EMPTY_FUNCTION),
1, // functions
EMPTY_FUNCTION(0), // --
ONE_EMPTY_FUNCTION,
SECTION(EXPORT_TABLE, 1 + 6),
1, // exports
FUNC_INDEX(0), // --