Commit d035d617 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Add an export table.

R=binji@chromium.org,jfb@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1744713003

Cr-Commit-Position: refs/heads/master@{#34342}
parent c1507e15
......@@ -55,6 +55,7 @@ class ModuleDecoder : public Decoder {
module->data_segments = new std::vector<WasmDataSegment>();
module->function_table = new std::vector<uint16_t>();
module->import_table = new std::vector<WasmImport>();
module->export_table = new std::vector<WasmExport>();
bool sections[kMaxModuleSectionCode];
memset(sections, 0, sizeof(sections));
......@@ -234,6 +235,34 @@ class ModuleDecoder : public Decoder {
}
break;
}
case kDeclExportTable: {
// Declares an export table.
CheckForPreviousSection(sections, kDeclFunctions, true);
int length;
uint32_t export_table_count =
consume_u32v(&length, "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, 0});
WasmExport* exp = &module->export_table->back();
const byte* sigpos = pc_;
exp->func_index = consume_u16("function index");
if (exp->func_index >= module->functions->size()) {
error(sigpos, sigpos,
"function index %u out of bounds (%d functions)",
exp->func_index,
static_cast<int>(module->functions->size()));
}
exp->name_offset = consume_string("export name");
}
break;
}
case kDeclWLL: {
// Reserved for experimentation by the Web Low-level Language project
// which is augmenting the binary encoding with source code meta
......
......@@ -283,7 +283,8 @@ WasmModule::WasmModule()
functions(nullptr),
data_segments(nullptr),
function_table(nullptr),
import_table(nullptr) {}
import_table(nullptr),
export_table(nullptr) {}
WasmModule::~WasmModule() {
if (globals) delete globals;
......@@ -292,6 +293,7 @@ WasmModule::~WasmModule() {
if (data_segments) delete data_segments;
if (function_table) delete function_table;
if (import_table) delete import_table;
if (export_table) delete export_table;
}
static MaybeHandle<JSFunction> LookupFunction(ErrorThrower& thrower,
......@@ -455,6 +457,32 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
instance.js_object->SetInternalField(kWasmModuleFunctionTable,
Smi::FromInt(0));
//-------------------------------------------------------------------------
// Create and populate the exports object.
//-------------------------------------------------------------------------
if (export_table->size() > 0) {
index = 0;
// 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);
// Compile wrappers and add them to the exports object.
for (const WasmExport& exp : *export_table) {
if (thrower.error()) break;
const char* cstr = GetName(exp.name_offset);
Handle<String> name = factory->InternalizeUtf8String(cstr);
Handle<Code> code = linker.GetFunctionCode(exp.func_index);
Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
isolate, &module_env, name, code, instance.js_object, exp.func_index);
JSObject::AddProperty(exports_object, name, function, READ_ONLY);
}
}
// Run the start function if one was specified.
if (this->start_function_index >= 0) {
HandleScope scope(isolate);
......
......@@ -33,6 +33,7 @@ enum WasmSectionDeclCode {
kDeclEnd = 0x06,
kDeclStartFunction = 0x07,
kDeclImportTable = 0x08,
kDeclExportTable = 0x09,
kDeclWLL = 0x11,
};
......@@ -74,6 +75,12 @@ struct WasmImport {
uint32_t function_name_offset; // offset in module bytes of the import name.
};
// Static representation of an exported WASM function.
struct WasmExport {
uint16_t func_index; // index into the function table.
uint32_t name_offset; // offset in module bytes of the name to export.
};
// Static representation of a wasm global variable.
struct WasmGlobal {
uint32_t name_offset; // offset in the module bytes of the name, if any.
......@@ -110,6 +117,7 @@ struct WasmModule {
std::vector<WasmDataSegment>* data_segments; // data segments in this module.
std::vector<uint16_t>* function_table; // function table.
std::vector<WasmImport>* import_table; // import table.
std::vector<WasmExport>* export_table; // export table.
WasmModule();
~WasmModule();
......
// Copyright 2016 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.
// Flags: --expose-wasm
load("test/mjsunit/wasm/wasm-constants.js");
(function testExportedMain() {
var kBodySize = 3;
var kReturnValue = 99;
var kNameMainOffset = 4 + 7 + kBodySize + 8 + 1;
var data = bytes(
// signatures
kDeclSignatures, 1,
0, kAstI32, // void -> i32
// -- main function
kDeclFunctions,
1,
0, // decl flags
0, 0, // signature index
kBodySize, 0,
// main body
kExprReturn,
kExprI8Const,
kReturnValue,
// exports
kDeclExportTable,
1,
0, 0, // func index index
kNameMainOffset, 0, 0, 0, // function name offset
// names
kDeclEnd,
'm', 'a', 'i', 'n', 0 // --
);
var ffi = new Object();
var module = _WASMEXP_.instantiateModule(data, ffi);
assertEquals("object", typeof module.exports);
assertEquals("function", typeof module.exports.main);
assertEquals(kReturnValue, module.exports.main());
})();
(function testExportedTwice() {
var kBodySize = 3;
var kReturnValue = 99;
var kNameMainOffset = 4 + 7 + kBodySize + 14 + 1;
var kNameFooOffset = kNameMainOffset + 5;
var data = bytes(
// signatures
kDeclSignatures, 1,
0, kAstI32, // void -> i32
// -- main function
kDeclFunctions,
1,
0, // decl flags
0, 0, // signature index
kBodySize, 0,
// main body
kExprReturn,
kExprI8Const,
kReturnValue,
// exports
kDeclExportTable,
2,
0, 0, // func index index
kNameMainOffset, 0, 0, 0, // function name offset
0, 0, // func index index
kNameFooOffset, 0, 0, 0, // function name offset
// names
kDeclEnd,
'b', 'l', 'a', 'h', 0, // --
'f', 'o', 'o', 0 // --
);
var ffi = new Object();
var module = _WASMEXP_.instantiateModule(data, ffi);
assertEquals("object", typeof module.exports);
assertEquals("function", typeof module.exports.blah);
assertEquals("function", typeof module.exports.foo);
assertEquals(kReturnValue, module.exports.blah());
assertEquals(kReturnValue, module.exports.foo());
})();
......@@ -24,6 +24,7 @@ var kDeclDataSegments = 0x04;
var kDeclFunctionTable = 0x05;
var kDeclStartFunction = 0x07;
var kDeclImportTable = 0x08;
var kDeclExportTable = 0x09;
var kDeclEnd = 0x06;
// Function declaration flags
......
......@@ -11,6 +11,21 @@ namespace v8 {
namespace internal {
namespace wasm {
#define VOID_VOID_SIG 0, kLocalVoid
#define INT_INT_SIG 1, kLocalI32, kLocalI32
#define U32(v) \
static_cast<byte>(v), static_cast<byte>(v >> 8), static_cast<byte>(v >> 16), \
static_cast<byte>(v >> 24)
#define U16(v) static_cast<byte>(v), static_cast<byte>(v >> 8)
#define U8(v) static_cast<byte>(v)
#define SIG_INDEX(v) U16(v)
#define FUNC_INDEX(v) U16(v)
#define NAME_OFFSET(v) U32(v)
#define EMPTY_FUNCTION(sig_index) 0, SIG_INDEX(sig_index), U16(0)
class WasmModuleVerifyTest : public TestWithZone {
public:
ModuleResult DecodeModule(const byte* module_start, const byte* module_end) {
......@@ -45,15 +60,6 @@ struct LocalTypePair {
{kLocalF64, kAstF64}};
// TODO(titzer): use these macros everywhere below.
#define U32_LE(v) \
static_cast<byte>(v), static_cast<byte>((v) >> 8), \
static_cast<byte>((v) >> 16), static_cast<byte>((v) >> 24)
#define U16_LE(v) static_cast<byte>(v), static_cast<byte>((v) >> 8)
TEST_F(WasmModuleVerifyTest, DecodeEmpty) {
static const byte data[1]{kDeclEnd};
{
......@@ -73,10 +79,7 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) {
static const byte data[] = {
kDeclGlobals,
1,
0,
0,
0,
0, // name offset
NAME_OFFSET(0),
kMemI32, // memory type
0, // exported
};
......@@ -135,9 +138,9 @@ static void AppendUint32v(std::vector<byte>& buffer, uint32_t val) {
TEST_F(WasmModuleVerifyTest, NGlobals) {
static const byte data[] = {
0, 0, 0, 0, // name offset
kMemI32, // memory type
0, // exported
NAME_OFFSET(0), // name offset
kMemI32, // memory type
0, // exported
};
for (uint32_t i = 0; i < 1000000; i = i * 7 + 1) {
std::vector<byte> buffer;
......@@ -157,13 +160,10 @@ TEST_F(WasmModuleVerifyTest, NGlobals) {
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) {
static const byte data[] = {
kDeclGlobals,
1, // declare one global
0,
3,
0,
0, // name offset
kMemI32, // memory type
0, // exported
1, // declare one global
NAME_OFFSET(300), // name offset
kMemI32, // memory type
0, // exported
};
EXPECT_FAILURE(data);
......@@ -173,13 +173,10 @@ TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) {
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) {
static const byte data[] = {
kDeclGlobals,
1, // declare one global
0,
0,
0,
0, // name offset
33, // memory type
0, // exported
1, // declare one global
NAME_OFFSET(0), // name offset
33, // memory type
0, // exported
};
EXPECT_FAILURE(data);
......@@ -190,18 +187,12 @@ TEST_F(WasmModuleVerifyTest, TwoGlobals) {
static const byte data[] = {
kDeclGlobals,
2,
0,
0,
0,
0, // #0: name offset
kMemF32, // memory type
0, // exported
0,
0,
0,
0, // #1: name offset
kMemF64, // memory type
1, // exported
NAME_OFFSET(0), // #0: name offset
kMemF32, // memory type
0, // exported
NAME_OFFSET(0), // #1: name offset
kMemF64, // memory type
1, // exported
};
{
......@@ -238,10 +229,15 @@ TEST_F(WasmModuleVerifyTest, TwoGlobals) {
TEST_F(WasmModuleVerifyTest, OneSignature) {
static const byte data[] = {
kDeclSignatures, 1, 0, kLocalVoid // void -> void
};
EXPECT_VERIFIES(data);
{
static const byte data[] = {kDeclSignatures, 1, VOID_VOID_SIG};
EXPECT_VERIFIES(data);
}
{
static const byte data[] = {kDeclSignatures, 1, INT_INT_SIG};
EXPECT_VERIFIES(data);
}
}
......@@ -287,16 +283,16 @@ TEST_F(WasmModuleVerifyTest, FunctionWithoutSig) {
static const byte data[] = {
kDeclFunctions, 1,
// func#0 ------------------------------------------------------
0, 0, // signature index
0, 0, 0, 0, // name offset
0, 0, 0, 0, // code start offset
0, 0, 0, 0, // code end offset
1, 2, // local int32 count
3, 4, // local int64 count
5, 6, // local float32 count
7, 8, // local float64 count
0, // exported
1 // external
SIG_INDEX(0), // signature index
NAME_OFFSET(0), // name offset
U32(0), // code start offset
U32(0), // code end offset
U16(899), // local int32 count
U16(799), // local int64 count
U16(699), // local float32 count
U16(599), // local float64 count
0, // exported
1 // external
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
......@@ -312,17 +308,17 @@ TEST_F(WasmModuleVerifyTest, OneEmptyVoidVoidFunction) {
static const byte data[] = {
kDeclSignatures, 1,
// sig#0 -------------------------------------------------------
0, 0, // void -> void
VOID_VOID_SIG,
// func#0 ------------------------------------------------------
kDeclFunctions, 1,
kDeclFunctionLocals | kDeclFunctionExport | kDeclFunctionName, 0,
0, // signature index
9, 0, 0, 0, // name offset
11, 2, // local int32 count
13, 4, // local int64 count
15, 6, // local float32 count
17, 8, // local float64 count
1, 0, // size
kDeclFunctionLocals | kDeclFunctionExport | kDeclFunctionName,
SIG_INDEX(0), // signature index
NAME_OFFSET(9), // name offset
U16(1466), // local int32 count
U16(1355), // local int64 count
U16(1244), // local float32 count
U16(1133), // local float64 count
1, 0, // size
kExprNop,
};
......@@ -342,10 +338,10 @@ TEST_F(WasmModuleVerifyTest, OneEmptyVoidVoidFunction) {
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
EXPECT_EQ(523, function->local_i32_count);
EXPECT_EQ(1037, function->local_i64_count);
EXPECT_EQ(1551, function->local_f32_count);
EXPECT_EQ(2065, function->local_f64_count);
EXPECT_EQ(1466, function->local_i32_count);
EXPECT_EQ(1355, function->local_i64_count);
EXPECT_EQ(1244, function->local_f32_count);
EXPECT_EQ(1133, function->local_f64_count);
EXPECT_TRUE(function->exported);
EXPECT_FALSE(function->external);
......@@ -366,11 +362,10 @@ TEST_F(WasmModuleVerifyTest, OneFunctionImported) {
static const byte data[] = {
kDeclSignatures, 1,
// sig#0 -------------------------------------------------------
0, 0, // void -> void
kDeclFunctions, 1,
VOID_VOID_SIG, kDeclFunctions, 1,
// func#0 ------------------------------------------------------
kDeclFunctionImport, // no name, no locals, imported
0, 0, // signature index
SIG_INDEX(0),
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
......@@ -643,9 +638,9 @@ TEST_F(WasmModuleVerifyTest, DataSegmentWithInvalidSource) {
1,
kDeclDataSegments,
1,
U32_LE(dest_addr),
U32_LE(source_offset),
U32_LE(source_size),
U32(dest_addr),
U32(source_offset),
U32(source_size),
1, // init
};
......@@ -678,9 +673,9 @@ TEST_F(WasmModuleVerifyTest, DataSegmentWithInvalidDest) {
1,
kDeclDataSegments,
1,
U32_LE(dest_addr),
U32_LE(source_offset),
U32_LE(source_size),
U32(dest_addr),
U32(source_offset),
U32(source_size),
1, // init
};
......@@ -1018,20 +1013,10 @@ TEST_F(WasmModuleVerifyTest, ImportTable_nosigs) {
TEST_F(WasmModuleVerifyTest, ImportTable_invalid_sig) {
static const byte data[] = {
kDeclSignatures,
0,
kDeclImportTable,
1,
0,
0, // sig index
1,
0,
0,
0, // module name
1,
0,
0,
0 // function name
kDeclSignatures, 0, kDeclImportTable, 1,
SIG_INDEX(0), // sig index
NAME_OFFSET(1), // module name
NAME_OFFSET(1), // function name
};
EXPECT_FAILURE(data);
}
......@@ -1040,42 +1025,22 @@ TEST_F(WasmModuleVerifyTest, ImportTable_one_sig) {
static const byte data[] = {
kDeclSignatures,
1,
0,
static_cast<byte>(kAstStmt),
VOID_VOID_SIG,
kDeclImportTable,
1,
0,
0, // sig index
1,
0,
0,
0, // module name
1,
0,
0,
0 // function name
1, // --
SIG_INDEX(0), // sig index
NAME_OFFSET(1), // module name
NAME_OFFSET(1) // function name
};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ImportTable_off_end) {
static const byte data[] = {
kDeclSignatures,
1,
0,
static_cast<byte>(kAstStmt),
kDeclImportTable,
1,
0,
0, // sig index
1,
0,
0,
0, // module name
1,
0,
0,
0 // function name
kDeclSignatures, 1, VOID_VOID_SIG, kDeclImportTable, 1,
SIG_INDEX(0), // sig index
NAME_OFFSET(1), // module name
NAME_OFFSET(1), // function name
};
for (size_t length = 5; length < sizeof(data); length++) {
......@@ -1085,6 +1050,109 @@ TEST_F(WasmModuleVerifyTest, ImportTable_off_end) {
}
}
TEST_F(WasmModuleVerifyTest, ExportTable_empty) {
static const byte data[] = {kDeclSignatures, 0, kDeclFunctions, 0,
kDeclExportTable, 0};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ExportTable_NoFunctions1) {
static const byte data[] = {kDeclSignatures, 0, kDeclExportTable, 0};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, ExportTable_NoFunctions2) {
static const byte data[] = {kDeclExportTable, 0};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, ExportTableOne) {
static const byte data[] = {
kDeclSignatures, 1, // sigs
VOID_VOID_SIG, // --
kDeclFunctions, 1, // functions
EMPTY_FUNCTION(0), // --
kDeclExportTable, 1, // exports
FUNC_INDEX(0), // --
NAME_OFFSET(0) // --
};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ExportTableTwo) {
static const byte data[] = {
kDeclSignatures, 1, // sigs
VOID_VOID_SIG, // --
kDeclFunctions, 1, // functions
EMPTY_FUNCTION(0), // --
kDeclExportTable, 2, // exports
FUNC_INDEX(0), // --
NAME_OFFSET(1), // --
FUNC_INDEX(0), // --
NAME_OFFSET(2) // --
};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ExportTableThree) {
static const byte data[] = {
kDeclSignatures, 1, // sigs
VOID_VOID_SIG, // --
kDeclFunctions, 3, // functions
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
kDeclExportTable, 3, // exports
FUNC_INDEX(0), // --
NAME_OFFSET(1), // --
FUNC_INDEX(1), // --
NAME_OFFSET(2), // --
FUNC_INDEX(2), // --
NAME_OFFSET(2) // --
};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ExportTableThreeOne) {
for (int i = 0; i < 6; i++) {
const byte data[] = {
kDeclSignatures, 1, // sigs
VOID_VOID_SIG, // --
kDeclFunctions, 3, // functions
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
EMPTY_FUNCTION(0), // --
kDeclExportTable, 1, // exports
FUNC_INDEX(i), // --
NAME_OFFSET(1) // --
};
if (i < 3) {
EXPECT_VERIFIES(data);
} else {
EXPECT_FAILURE(data);
}
}
}
TEST_F(WasmModuleVerifyTest, ExportTableOne_off_end) {
static const byte data[] = {
kDeclSignatures, 1, // sigs
VOID_VOID_SIG, // --
kDeclFunctions, 1, // functions
EMPTY_FUNCTION(0), // --
kDeclExportTable, 1, // exports
FUNC_INDEX(0), // --
NAME_OFFSET(0) // --
};
for (int length = 13; length < sizeof(data); length++) {
ModuleResult result = DecodeModule(data, data + length);
EXPECT_FALSE(result.ok());
if (result.val) delete result.val;
}
}
} // namespace wasm
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment