Commit 915ae08d authored by titzer's avatar titzer Committed by Commit bot

[wasm] Add support for import section.

This CL introduces an import section that names functions to be imported
as well as a CallImport bytecode to call imports from this table.

R=binji@chromium.org,bradnelson@chromium.org
LOG=Y
BUG=chromium:575167

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

Cr-Commit-Position: refs/heads/master@{#34157}
parent a2ceaa23
......@@ -1504,6 +1504,15 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args) {
return BuildWasmCall(sig, args);
}
Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args) {
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = Constant(module_->GetImportCode(index));
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
return BuildWasmCall(sig, args);
}
Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args) {
DCHECK_NOT_NULL(args[0]);
......@@ -2038,12 +2047,9 @@ Handle<JSFunction> CompileJSToWasmWrapper(
return function;
}
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
Handle<JSFunction> function,
uint32_t index) {
wasm::WasmFunction* func = &module->module->functions->at(index);
wasm::FunctionSig* sig, const char* name) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
......@@ -2057,11 +2063,11 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
Node* control = nullptr;
Node* effect = nullptr;
WasmGraphBuilder builder(&zone, &jsgraph, func->sig);
WasmGraphBuilder builder(&zone, &jsgraph, sig);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.set_module(module);
builder.BuildWasmToJSWrapper(function, func->sig);
builder.BuildWasmToJSWrapper(function, sig);
Handle<Code> code = Handle<Code>::null();
{
......@@ -2086,7 +2092,7 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
}
// Schedule and compile to machine code.
CallDescriptor* incoming = module->GetWasmCallDescriptor(&zone, func->sig);
CallDescriptor* incoming = module->GetWasmCallDescriptor(&zone, sig);
// TODO(titzer): this is technically a WASM wrapper, not a wasm function.
Code::Flags flags = Code::ComputeFlags(Code::WASM_FUNCTION);
bool debugging =
......@@ -2110,8 +2116,8 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
buffer.Dispose();
}
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "wasm-to-js", index,
module->module->GetName(func->name_offset));
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "wasm-to-js", 0,
name);
}
return code;
}
......
......@@ -40,7 +40,7 @@ Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate,
// Wraps a JS function, producing a code object that can be called from WASM.
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
Handle<JSFunction> function,
uint32_t index);
wasm::FunctionSig* sig, const char* name);
// Wraps a given wasm code object, producing a JSFunction that can be called
// from JavaScript.
......@@ -100,6 +100,7 @@ class WasmGraphBuilder {
Node* Unreachable();
Node* CallDirect(uint32_t index, Node** args);
Node* CallImport(uint32_t index, Node** args);
Node* CallIndirect(uint32_t index, Node** args);
void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSFunction> function,
......
......@@ -175,6 +175,16 @@ class WasmDecoder : public Decoder {
return false;
}
inline bool Validate(const byte* pc, ImportIndexOperand& operand) {
ModuleEnv* m = function_env_->module;
if (m && m->module && operand.index < m->module->import_table->size()) {
operand.sig = m->module->import_table->at(operand.index).sig;
return true;
}
error(pc, pc + 1, "invalid signature index");
return false;
}
inline bool Validate(const byte* pc, BreakDepthOperand& operand,
ZoneVector<Block>& blocks) {
if (operand.depth < blocks.size()) {
......@@ -261,6 +271,12 @@ class WasmDecoder : public Decoder {
function_env_->module->GetSignature(operand.index)
->parameter_count());
}
case kExprCallImport: {
ImportIndexOperand operand(this, pc);
return static_cast<int>(
function_env_->module->GetImportSignature(operand.index)
->parameter_count());
}
case kExprReturn: {
return static_cast<int>(function_env_->sig->return_count());
}
......@@ -317,6 +333,10 @@ class WasmDecoder : public Decoder {
SignatureIndexOperand operand(this, pc);
return 1 + operand.length;
}
case kExprCallImport: {
ImportIndexOperand operand(this, pc);
return 1 + operand.length;
}
case kExprSetLocal:
case kExprGetLocal: {
......@@ -790,6 +810,17 @@ class LR_WasmDecoder : public WasmDecoder {
len = 1 + operand.length;
break;
}
case kExprCallImport: {
ImportIndexOperand operand(this, pc_);
if (Validate(pc_, operand)) {
LocalType type = operand.sig->return_count() == 0
? kAstStmt
: operand.sig->GetReturn();
Shift(type, static_cast<int>(operand.sig->parameter_count()));
}
len = 1 + operand.length;
break;
}
default:
error("Invalid opcode");
return;
......@@ -1212,6 +1243,23 @@ class LR_WasmDecoder : public WasmDecoder {
}
break;
}
case kExprCallImport: {
ImportIndexOperand operand(this, p->pc());
CHECK(Validate(p->pc(), operand));
if (p->index > 0) {
TypeCheckLast(p, operand.sig->GetParam(p->index - 1));
}
if (p->done() && build()) {
uint32_t count = p->tree->count + 1;
TFNode** buffer = builder_->Buffer(count);
buffer[0] = nullptr; // reserved for code object.
for (uint32_t i = 1; i < count; i++) {
buffer[i] = p->tree->children[i - 1]->node;
}
p->tree->node = builder_->CallImport(operand.index, buffer);
}
break;
}
default:
break;
}
......
......@@ -132,6 +132,16 @@ struct FunctionIndexOperand {
}
};
struct ImportIndexOperand {
uint32_t index;
FunctionSig* sig;
int length;
inline ImportIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "import index");
sig = nullptr;
}
};
struct TableSwitchOperand {
uint32_t case_count;
uint32_t table_count;
......
......@@ -54,6 +54,7 @@ class ModuleDecoder : public Decoder {
module->functions = new std::vector<WasmFunction>();
module->data_segments = new std::vector<WasmDataSegment>();
module->function_table = new std::vector<uint16_t>();
module->import_table = new std::vector<WasmImport>();
bool sections[kMaxModuleSectionCode];
memset(sections, 0, sizeof(sections));
......@@ -203,6 +204,36 @@ class ModuleDecoder : public Decoder {
}
break;
}
case kDeclImportTable: {
// Declares an import table.
CheckForPreviousSection(sections, kDeclSignatures, true);
int length;
uint32_t import_table_count =
consume_u32v(&length, "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, 0, 0});
WasmImport* import = &module->import_table->back();
const byte* sigpos = pc_;
import->sig_index = consume_u16("signature index");
if (import->sig_index >= module->signatures->size()) {
error(sigpos, "invalid signature index");
} else {
import->sig = module->signatures->at(import->sig_index);
}
import->module_name_offset = consume_string("import module name");
import->function_name_offset =
consume_string("import function name");
}
break;
}
case kDeclWLL: {
// Reserved for experimentation by the Web Low-level Language project
// which is augmenting the binary encoding with source code meta
......
......@@ -104,9 +104,12 @@
static_cast<byte>(offset), index, val
#define WASM_CALL_FUNCTION(index, ...) \
kExprCallFunction, static_cast<byte>(index), __VA_ARGS__
#define WASM_CALL_IMPORT(index, ...) \
kExprCallImport, static_cast<byte>(index), __VA_ARGS__
#define WASM_CALL_INDIRECT(index, func, ...) \
kExprCallIndirect, static_cast<byte>(index), func, __VA_ARGS__
#define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index)
#define WASM_CALL_IMPORT0(index) kExprCallImport, static_cast<byte>(index)
#define WASM_CALL_INDIRECT0(index, func) \
kExprCallIndirect, static_cast<byte>(index), func
#define WASM_NOT(x) kExprBoolNot, x
......
......@@ -282,7 +282,8 @@ WasmModule::WasmModule()
signatures(nullptr),
functions(nullptr),
data_segments(nullptr),
function_table(nullptr) {}
function_table(nullptr),
import_table(nullptr) {}
WasmModule::~WasmModule() {
if (globals) delete globals;
......@@ -290,8 +291,33 @@ WasmModule::~WasmModule() {
if (functions) delete functions;
if (data_segments) delete data_segments;
if (function_table) delete function_table;
if (import_table) delete import_table;
}
static MaybeHandle<JSFunction> LookupFunction(ErrorThrower& thrower,
Handle<JSObject> ffi,
uint32_t index,
Handle<String> name,
const char* cstr) {
if (!ffi.is_null()) {
MaybeHandle<Object> result = Object::GetProperty(ffi, name);
if (!result.is_null()) {
Handle<Object> obj = result.ToHandleChecked();
if (obj->IsJSFunction()) {
return Handle<JSFunction>::cast(obj);
} else {
thrower.Error("FFI function #%d:%s is not a JSFunction.", index, cstr);
return MaybeHandle<JSFunction>();
}
} else {
thrower.Error("FFI function #%d:%s not found.", index, cstr);
return MaybeHandle<JSFunction>();
}
} else {
thrower.Error("FFI table is not an object.");
return MaybeHandle<JSFunction>();
}
}
// Instantiates a wasm module as a JSObject.
// * allocates a backing store of {mem_size} bytes.
......@@ -312,6 +338,7 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
JS_OBJECT_TYPE,
JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
WasmModuleInstance instance(this);
std::vector<Handle<Code>> import_code;
instance.context = isolate->native_context();
instance.js_object = factory->NewJSObjectFromMap(map, TENURED);
Handle<FixedArray> code_table =
......@@ -351,10 +378,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
}
//-------------------------------------------------------------------------
// Compile all functions in the module.
// Compile wrappers to imported functions.
//-------------------------------------------------------------------------
instance.function_table = BuildFunctionTable(isolate, this);
uint32_t index = 0;
instance.function_table = BuildFunctionTable(isolate, this);
WasmLinker linker(isolate, functions->size());
ModuleEnv module_env;
module_env.module = this;
......@@ -362,7 +389,28 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
module_env.linker = &linker;
module_env.asm_js = false;
if (import_table->size() > 0) {
instance.import_code = &import_code;
instance.import_code->reserve(import_table->size());
for (const WasmImport& import : *import_table) {
const char* cstr = GetName(import.function_name_offset);
Handle<String> name = factory->InternalizeUtf8String(cstr);
MaybeHandle<JSFunction> function =
LookupFunction(thrower, ffi, index, name, cstr);
if (function.is_null()) return MaybeHandle<JSObject>();
Handle<Code> code = compiler::CompileWasmToJSWrapper(
isolate, &module_env, function.ToHandleChecked(), import.sig, cstr);
instance.import_code->push_back(code);
index++;
}
}
//-------------------------------------------------------------------------
// Compile all functions in the module.
//-------------------------------------------------------------------------
// First pass: compile each function and initialize the code table.
index = 0;
for (const WasmFunction& func : *functions) {
if (thrower.error()) break;
DCHECK_EQ(index, func.func_index);
......@@ -373,27 +421,11 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
Handle<JSFunction> function = Handle<JSFunction>::null();
if (func.external) {
// Lookup external function in FFI object.
if (!ffi.is_null()) {
MaybeHandle<Object> result = Object::GetProperty(ffi, name);
if (!result.is_null()) {
Handle<Object> obj = result.ToHandleChecked();
if (obj->IsJSFunction()) {
function = Handle<JSFunction>::cast(obj);
code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
function, index);
} else {
thrower.Error("FFI function #%d:%s is not a JSFunction.", index,
cstr);
return MaybeHandle<JSObject>();
}
} else {
thrower.Error("FFI function #%d:%s not found.", index, cstr);
return MaybeHandle<JSObject>();
}
} else {
thrower.Error("FFI table is not an object.");
return MaybeHandle<JSObject>();
}
MaybeHandle<JSFunction> function =
LookupFunction(thrower, ffi, index, name, cstr);
if (function.is_null()) return MaybeHandle<JSObject>();
code = compiler::CompileWasmToJSWrapper(
isolate, &module_env, function.ToHandleChecked(), func.sig, cstr);
} else {
// Compile the function.
code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func);
......@@ -454,6 +486,13 @@ Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) {
return Handle<Code>::null();
}
Handle<Code> ModuleEnv::GetImportCode(uint32_t index) {
DCHECK(IsValidImport(index));
if (instance && instance->import_code) {
return instance->import_code->at(index);
}
return Handle<Code>::null();
}
compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
uint32_t index) {
......
......@@ -30,12 +30,13 @@ enum WasmSectionDeclCode {
kDeclGlobals = 0x03,
kDeclDataSegments = 0x04,
kDeclFunctionTable = 0x05,
kDeclWLL = 0x11,
kDeclEnd = 0x06,
kDeclStartFunction = 0x07,
kDeclImportTable = 0x08,
kDeclWLL = 0x11,
};
static const int kMaxModuleSectionCode = 6;
static const int kMaxModuleSectionCode = 0x11;
enum WasmFunctionDeclBit {
kDeclFunctionName = 0x01,
......@@ -49,7 +50,7 @@ static const size_t kDeclMemorySize = 3;
static const size_t kDeclGlobalSize = 6;
static const size_t kDeclDataSegmentSize = 13;
// Static representation of a wasm function.
// Static representation of a WASM function.
struct WasmFunction {
FunctionSig* sig; // signature of the function.
uint32_t func_index; // index into the function table.
......@@ -65,7 +66,13 @@ struct WasmFunction {
bool external; // true if this function is externally supplied.
};
struct ModuleEnv; // forward declaration of decoder interface.
// Static representation of an imported WASM function.
struct WasmImport {
FunctionSig* sig; // signature of the function.
uint16_t sig_index; // index into the signature table.
uint32_t module_name_offset; // offset in module bytes of the module name.
uint32_t function_name_offset; // offset in module bytes of the import name.
};
// Static representation of a wasm global variable.
struct WasmGlobal {
......@@ -102,6 +109,7 @@ struct WasmModule {
std::vector<WasmFunction>* functions; // functions in this module.
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.
WasmModule();
~WasmModule();
......@@ -134,6 +142,7 @@ struct WasmModuleInstance {
Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
Handle<FixedArray> function_table; // indirect function table.
std::vector<Handle<Code>>* function_code; // code objects for each function.
std::vector<Handle<Code>>* import_code; // code objects for each import.
// -- raw memory ------------------------------------------------------------
byte* mem_start; // start of linear memory.
size_t mem_size; // size of the linear memory.
......@@ -170,6 +179,9 @@ struct ModuleEnv {
bool IsValidSignature(uint32_t index) {
return module && index < module->signatures->size();
}
bool IsValidImport(uint32_t index) {
return module && index < module->import_table->size();
}
MachineType GetGlobalType(uint32_t index) {
DCHECK(IsValidGlobal(index));
return module->globals->at(index).type;
......@@ -178,6 +190,10 @@ struct ModuleEnv {
DCHECK(IsValidFunction(index));
return module->functions->at(index).sig;
}
FunctionSig* GetImportSignature(uint32_t index) {
DCHECK(IsValidImport(index));
return module->import_table->at(index).sig;
}
FunctionSig* GetSignature(uint32_t index) {
DCHECK(IsValidSignature(index));
return module->signatures->at(index);
......@@ -188,6 +204,7 @@ struct ModuleEnv {
}
Handle<Code> GetFunctionCode(uint32_t index);
Handle<Code> GetImportCode(uint32_t index);
Handle<FixedArray> GetFunctionTable();
compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, FunctionSig* sig);
......
......@@ -68,6 +68,8 @@ struct MemoryAccess {
typedef Signature<LocalType> FunctionSig;
std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
// TODO(titzer): Renumber all the opcodes to fill in holes.
// Control expressions and blocks.
#define FOREACH_CONTROL_OPCODE(V) \
V(Nop, 0x00, _) \
......@@ -81,7 +83,6 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(TableSwitch, 0x08, _) \
V(Return, 0x14, _) \
V(Unreachable, 0x15, _)
// TODO(titzer): numbering
// Constants, locals, globals, and calls.
#define FOREACH_MISC_OPCODE(V) \
......@@ -95,7 +96,8 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(LoadGlobal, 0x10, _) \
V(StoreGlobal, 0x11, _) \
V(CallFunction, 0x12, _) \
V(CallIndirect, 0x13, _)
V(CallIndirect, 0x13, _) \
V(CallImport, 0x1F, _)
// Load memory expressions.
#define FOREACH_LOAD_MEM_OPCODE(V) \
......
......@@ -56,7 +56,8 @@ uint32_t AddJsFunction(TestingModule* module, FunctionSig* sig,
module->AddFunction(sig, Handle<Code>::null());
uint32_t index = static_cast<uint32_t>(module->module->functions->size() - 1);
Isolate* isolate = CcTest::InitIsolateOnce();
Handle<Code> code = CompileWasmToJSWrapper(isolate, module, jsfunc, index);
Handle<Code> code =
CompileWasmToJSWrapper(isolate, module, jsfunc, sig, "test");
module->instance->function_code->at(index) = code;
return index;
}
......
// 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.
// Flags: --expose-wasm
load("test/mjsunit/wasm/wasm-constants.js");
function testCallImport(func, check) {
var kBodySize = 6;
var kNameFunOffset = 29 + kBodySize + 1;
var kNameMainOffset = kNameFunOffset + 4;
var ffi = new Object();
ffi.fun = func;
var data = bytes(
// signatures
kDeclSignatures, 1,
2, kAstI32, kAstF64, kAstF64, // (f64,f64) -> int
// -- main function
kDeclFunctions,
1,
kDeclFunctionName | kDeclFunctionExport,
0, 0,
kNameMainOffset, 0, 0, 0, // name offset
kBodySize, 0,
// main body
kExprCallImport, 0, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
// imports
kDeclImportTable,
1,
0, 0, // sig index
0, 0, 0, 0, // module name offset
kNameFunOffset, 0, 0, 0, // function name offset
// names
kDeclEnd,
'f', 'u', 'n', 0, // --
'm', 'a', 'i', 'n', 0 // --
);
var module = _WASMEXP_.instantiateModule(data, ffi);
assertEquals("function", typeof module.main);
for (var i = 0; i < 100000; i += 10003) {
var a = 22.5 + i, b = 10.5 + i;
var r = module.main(a, b);
check(r, a, b);
}
}
var global = (function() { return this; })();
var params = [-99, -99, -99, -99];
var was_called = false;
var length = -1;
function FOREIGN_SUB(a, b) {
print("FOREIGN_SUB(" + a + ", " + b + ")");
was_called = true;
params[0] = this;
params[1] = a;
params[2] = b;
return (a - b) | 0;
}
function check_FOREIGN_SUB(r, a, b) {
assertEquals(a - b | 0, r);
assertTrue(was_called);
// assertEquals(global, params[0]); // sloppy mode
assertEquals(a, params[1]);
assertEquals(b, params[2]);
was_called = false;
}
testCallImport(FOREIGN_SUB, check_FOREIGN_SUB);
function FOREIGN_ABCD(a, b, c, d) {
print("FOREIGN_ABCD(" + a + ", " + b + ", " + c + ", " + d + ")");
was_called = true;
params[0] = this;
params[1] = a;
params[2] = b;
params[3] = c;
params[4] = d;
return (a * b * 6) | 0;
}
function check_FOREIGN_ABCD(r, a, b) {
assertEquals((a * b * 6) | 0, r);
assertTrue(was_called);
// assertEquals(global, params[0]); // sloppy mode.
assertEquals(a, params[1]);
assertEquals(b, params[2]);
assertEquals(undefined, params[3]);
assertEquals(undefined, params[4]);
was_called = false;
}
testCallImport(FOREIGN_ABCD, check_FOREIGN_ABCD);
function FOREIGN_ARGUMENTS0() {
print("FOREIGN_ARGUMENTS0");
was_called = true;
length = arguments.length;
for (var i = 0; i < arguments.length; i++) {
params[i] = arguments[i];
}
return (arguments[0] * arguments[1] * 7) | 0;
}
function FOREIGN_ARGUMENTS1(a) {
print("FOREIGN_ARGUMENTS1", a);
was_called = true;
length = arguments.length;
for (var i = 0; i < arguments.length; i++) {
params[i] = arguments[i];
}
return (arguments[0] * arguments[1] * 7) | 0;
}
function FOREIGN_ARGUMENTS2(a, b) {
print("FOREIGN_ARGUMENTS2", a, b);
was_called = true;
length = arguments.length;
for (var i = 0; i < arguments.length; i++) {
params[i] = arguments[i];
}
return (a * b * 7) | 0;
}
function FOREIGN_ARGUMENTS3(a, b, c) {
print("FOREIGN_ARGUMENTS3", a, b, c);
was_called = true;
length = arguments.length;
for (var i = 0; i < arguments.length; i++) {
params[i] = arguments[i];
}
return (a * b * 7) | 0;
}
function FOREIGN_ARGUMENTS4(a, b, c, d) {
print("FOREIGN_ARGUMENTS4", a, b, c, d);
was_called = true;
length = arguments.length;
for (var i = 0; i < arguments.length; i++) {
params[i] = arguments[i];
}
return (a * b * 7) | 0;
}
function check_FOREIGN_ARGUMENTS(r, a, b) {
assertEquals((a * b * 7) | 0, r);
assertTrue(was_called);
assertEquals(2, length);
assertEquals(a, params[0]);
assertEquals(b, params[1]);
was_called = false;
}
// Check a bunch of uses of the arguments object.
testCallImport(FOREIGN_ARGUMENTS0, check_FOREIGN_ARGUMENTS);
testCallImport(FOREIGN_ARGUMENTS1, check_FOREIGN_ARGUMENTS);
testCallImport(FOREIGN_ARGUMENTS2, check_FOREIGN_ARGUMENTS);
testCallImport(FOREIGN_ARGUMENTS3, check_FOREIGN_ARGUMENTS);
testCallImport(FOREIGN_ARGUMENTS4, check_FOREIGN_ARGUMENTS);
function returnValue(val) {
return function(a, b) {
print("RETURN_VALUE ", val);
return val;
}
}
function checkReturn(expected) {
return function(r, a, b) { assertEquals(expected, r); }
}
// Check that returning weird values doesn't crash
testCallImport(returnValue(undefined), checkReturn(0));
testCallImport(returnValue(null), checkReturn(0));
testCallImport(returnValue("0"), checkReturn(0));
testCallImport(returnValue("-77"), checkReturn(-77));
var objWithValueOf = {valueOf: function() { return 198; }}
testCallImport(returnValue(objWithValueOf), checkReturn(198));
function testCallBinopVoid(type, func, check) {
var kBodySize = 10;
var kNameFunOffset = 28 + kBodySize + 1;
var kNameMainOffset = kNameFunOffset + 4;
var ffi = new Object();
var passed_length = -1;
var passed_a = -1;
var passed_b = -1;
var args_a = -1;
var args_b = -1;
ffi.fun = function(a, b) {
passed_length = arguments.length;
passed_a = a;
passed_b = b;
args_a = arguments[0];
args_b = arguments[1];
}
var data = bytes(
// -- signatures
kDeclSignatures, 2,
2, kAstStmt, type, type, // (type,type)->void
2, kAstI32, type, type, // (type,type)->int
// -- foreign function
kDeclFunctions, 2,
kDeclFunctionName | kDeclFunctionImport,
0, 0, // signature index
kNameFunOffset, 0, 0, 0, // name offset
// -- main function
kDeclFunctionName | kDeclFunctionExport,
1, 0, // signature index
kNameMainOffset, 0, 0, 0, // name offset
kBodySize, 0, // body size
// main body
kExprBlock, 2, // --
kExprCallFunction, 0, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI8Const, 99, // --
// names
kDeclEnd,
'f', 'u', 'n', 0, // --
'm', 'a', 'i', 'n', 0 // --
);
var module = _WASMEXP_.instantiateModule(data, ffi);
assertEquals("function", typeof module.main);
print("testCallBinopVoid", type);
for (var i = 0; i < 100000; i += 10003.1) {
var a = 22.5 + i, b = 10.5 + i;
var r = module.main(a, b);
assertEquals(99, r);
assertEquals(2, passed_length);
var expected_a, expected_b;
switch (type) {
case kAstI32: {
expected_a = a | 0;
expected_b = b | 0;
break;
}
case kAstF32: {
expected_a = Math.fround(a);
expected_b = Math.fround(b);
break;
}
case kAstF64: {
expected_a = a;
expected_b = b;
break;
}
}
assertEquals(expected_a, args_a);
assertEquals(expected_b, args_b);
assertEquals(expected_a, passed_a);
assertEquals(expected_b, passed_b);
}
}
testCallBinopVoid(kAstI32);
// TODO testCallBinopVoid(kAstI64);
testCallBinopVoid(kAstF32);
testCallBinopVoid(kAstF64);
function testCallPrint() {
var kBodySize = 10;
var kNamePrintOffset = 10 + 7 + 7 + 9 + kBodySize + 1;
var kNameMainOffset = kNamePrintOffset + 6;
var ffi = new Object();
ffi.print = print;
var data = bytes(
// -- signatures
kDeclSignatures, 2,
1, kAstStmt, kAstI32, // i32->void
1, kAstStmt, kAstF64, // f64->int
kDeclFunctions, 3,
// -- import print i32
kDeclFunctionName | kDeclFunctionImport,
0, 0, // signature index
kNamePrintOffset, 0, 0, 0, // name offset
// -- import print f64
kDeclFunctionName | kDeclFunctionImport,
1, 0, // signature index
kNamePrintOffset, 0, 0, 0, // name offset
// -- decl main
kDeclFunctionName | kDeclFunctionExport,
1, 0, // signature index
kNameMainOffset, 0, 0, 0, // name offset
kBodySize, 0, // body size
// main body
kExprBlock, 2, // --
kExprCallFunction, 0, // --
kExprI8Const, 97, // --
kExprCallFunction, 1, // --
kExprGetLocal, 0, // --
// names
kDeclEnd,
'p', 'r', 'i', 'n', 't', 0, // --
'm', 'a', 'i', 'n', 0 // --
);
var module = _WASMEXP_.instantiateModule(data, ffi);
assertEquals("function", typeof module.main);
for (var i = -9; i < 900; i += 6.125) {
module.main(i);
}
}
testCallPrint();
testCallPrint();
......@@ -23,6 +23,7 @@ var kDeclGlobals = 0x03;
var kDeclDataSegments = 0x04;
var kDeclFunctionTable = 0x05;
var kDeclStartFunction = 0x07;
var kDeclImportTable = 0x08;
var kDeclEnd = 0x06;
// Function declaration flags
......@@ -62,6 +63,7 @@ var kExprLoadGlobal = 0x10;
var kExprStoreGlobal = 0x11;
var kExprCallFunction = 0x12;
var kExprCallIndirect = 0x13;
var kExprCallImport = 0x1F;
var kExprI32LoadMem8S = 0x20;
var kExprI32LoadMem8U = 0x21;
......
......@@ -1192,6 +1192,7 @@ class TestModuleEnv : public ModuleEnv {
mod.globals = new std::vector<WasmGlobal>;
mod.signatures = new std::vector<FunctionSig*>;
mod.functions = new std::vector<WasmFunction>;
mod.import_table = new std::vector<WasmImport>;
}
byte AddGlobal(MachineType mem_type) {
mod.globals->push_back({0, mem_type, 0, false});
......@@ -1208,6 +1209,11 @@ class TestModuleEnv : public ModuleEnv {
CHECK(mod.functions->size() <= 127);
return static_cast<byte>(mod.functions->size() - 1);
}
byte AddImport(FunctionSig* sig) {
mod.import_table->push_back({sig, 0, 0});
CHECK(mod.import_table->size() <= 127);
return static_cast<byte>(mod.import_table->size() - 1);
}
private:
WasmModule mod;
......@@ -1364,6 +1370,39 @@ TEST_F(WasmDecoderTest, IndirectCallsWithMismatchedSigs3) {
EXPECT_FAILURE_INLINE(env, WASM_CALL_INDIRECT(f1, WASM_ZERO, WASM_F32(17.6)));
}
TEST_F(WasmDecoderTest, SimpleImportCalls) {
FunctionEnv* env = &env_i_i;
TestModuleEnv module_env;
env->module = &module_env;
byte f0 = module_env.AddImport(sigs.i_v());
byte f1 = module_env.AddImport(sigs.i_i());
byte f2 = module_env.AddImport(sigs.i_ii());
EXPECT_VERIFIES_INLINE(env, WASM_CALL_IMPORT0(f0));
EXPECT_VERIFIES_INLINE(env, WASM_CALL_IMPORT(f1, WASM_I8(22)));
EXPECT_VERIFIES_INLINE(env, WASM_CALL_IMPORT(f2, WASM_I8(32), WASM_I8(72)));
}
TEST_F(WasmDecoderTest, ImportCallsWithMismatchedSigs3) {
FunctionEnv* env = &env_i_i;
TestModuleEnv module_env;
env->module = &module_env;
byte f0 = module_env.AddImport(sigs.i_f());
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT0(f0));
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT(f0, WASM_I8(17)));
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT(f0, WASM_I64(27)));
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT(f0, WASM_F64(37.2)));
byte f1 = module_env.AddImport(sigs.i_d());
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT0(f1));
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT(f1, WASM_I8(16)));
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT(f1, WASM_I64(16)));
EXPECT_FAILURE_INLINE(env, WASM_CALL_IMPORT(f1, WASM_F32(17.6)));
}
TEST_F(WasmDecoderTest, Int32Globals) {
FunctionEnv* env = &env_i_i;
......@@ -1987,6 +2026,7 @@ TEST_F(WasmOpcodeLengthTest, MiscExpressions) {
EXPECT_LENGTH(2, kExprLoadGlobal);
EXPECT_LENGTH(2, kExprStoreGlobal);
EXPECT_LENGTH(2, kExprCallFunction);
EXPECT_LENGTH(2, kExprCallImport);
EXPECT_LENGTH(2, kExprCallIndirect);
EXPECT_LENGTH(1, kExprIf);
EXPECT_LENGTH(1, kExprIfElse);
......@@ -2237,12 +2277,16 @@ TEST_F(WasmOpcodeArityTest, Calls) {
module.AddSignature(sigs.f_ff());
module.AddSignature(sigs.i_d());
module.AddImport(sigs.f_ff());
module.AddImport(sigs.i_d());
{
FunctionEnv env;
WasmDecoderTest::init_env(&env, sigs.i_ii());
env.module = &module;
EXPECT_ARITY(2, kExprCallFunction, 0);
EXPECT_ARITY(2, kExprCallImport, 0);
EXPECT_ARITY(3, kExprCallIndirect, 0);
EXPECT_ARITY(1, kExprBr);
EXPECT_ARITY(2, kExprBrIf);
......@@ -2254,6 +2298,7 @@ TEST_F(WasmOpcodeArityTest, Calls) {
env.module = &module;
EXPECT_ARITY(1, kExprCallFunction, 1);
EXPECT_ARITY(1, kExprCallImport, 1);
EXPECT_ARITY(2, kExprCallIndirect, 1);
EXPECT_ARITY(1, kExprBr);
EXPECT_ARITY(2, kExprBrIf);
......
......@@ -70,7 +70,7 @@ TEST_F(WasmModuleVerifyTest, DecodeEmpty) {
TEST_F(WasmModuleVerifyTest, OneGlobal) {
const byte data[] = {
static const byte data[] = {
kDeclGlobals,
1,
0,
......@@ -109,7 +109,7 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) {
TEST_F(WasmModuleVerifyTest, ZeroGlobals) {
const byte data[] = {
static const byte data[] = {
kDeclGlobals, 0, // declare 0 globals
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
......@@ -134,7 +134,7 @@ static void AppendUint32v(std::vector<byte>& buffer, uint32_t val) {
TEST_F(WasmModuleVerifyTest, NGlobals) {
const byte data[] = {
static const byte data[] = {
0, 0, 0, 0, // name offset
kMemI32, // memory type
0, // exported
......@@ -155,7 +155,7 @@ TEST_F(WasmModuleVerifyTest, NGlobals) {
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) {
const byte data[] = {
static const byte data[] = {
kDeclGlobals,
1, // declare one global
0,
......@@ -171,7 +171,7 @@ TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) {
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) {
const byte data[] = {
static const byte data[] = {
kDeclGlobals,
1, // declare one global
0,
......@@ -187,7 +187,7 @@ TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) {
TEST_F(WasmModuleVerifyTest, TwoGlobals) {
const byte data[] = {
static const byte data[] = {
kDeclGlobals,
2,
0,
......@@ -904,7 +904,7 @@ class WasmFunctionVerifyTest : public TestWithZone {};
TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) {
byte data[] = {
static const byte data[] = {
0, kLocalVoid, // signature
3, 0, // local int32 count
4, 0, // local int64 count
......@@ -945,7 +945,7 @@ TEST_F(WasmModuleVerifyTest, WLLSectionNoLen) {
TEST_F(WasmModuleVerifyTest, WLLSectionEmpty) {
const byte data[] = {
static const byte data[] = {
kDeclWLL, 0, // empty section
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
......@@ -955,7 +955,7 @@ TEST_F(WasmModuleVerifyTest, WLLSectionEmpty) {
TEST_F(WasmModuleVerifyTest, WLLSectionOne) {
const byte data[] = {
static const byte data[] = {
kDeclWLL,
1, // LEB128 1
0, // one byte section
......@@ -967,10 +967,10 @@ TEST_F(WasmModuleVerifyTest, WLLSectionOne) {
TEST_F(WasmModuleVerifyTest, WLLSectionTen) {
const byte data[] = {
static const byte data[] = {
kDeclWLL,
10, // LEB128 10
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 10 byte section
10, // LEB128 10
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 10 byte section
};
ModuleResult result = DecodeModule(data, data + arraysize(data));
EXPECT_TRUE(result.ok());
......@@ -979,20 +979,19 @@ TEST_F(WasmModuleVerifyTest, WLLSectionTen) {
TEST_F(WasmModuleVerifyTest, WLLSectionOverflow) {
const byte data[] = {
static const byte data[] = {
kDeclWLL,
11, // LEB128 11
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 10 byte section
11, // LEB128 11
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 10 byte section
};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, WLLSectionUnderflow) {
const byte data[] = {
kDeclWLL,
0xff, 0xff, 0xff, 0xff, 0x0f, // LEB128 0xffffffff
1, 2, 3, 4, // 4 byte section
static const byte data[] = {
kDeclWLL, 0xff, 0xff, 0xff, 0xff, 0x0f, // LEB128 0xffffffff
1, 2, 3, 4, // 4 byte section
};
EXPECT_FAILURE(data);
}
......@@ -1000,14 +999,92 @@ TEST_F(WasmModuleVerifyTest, WLLSectionUnderflow) {
TEST_F(WasmModuleVerifyTest, WLLSectionLoop) {
// Would infinite loop decoding if wrapping and allowed.
const byte data[] = {
kDeclWLL,
0xfa, 0xff, 0xff, 0xff, 0x0f, // LEB128 0xfffffffa
1, 2, 3, 4, // 4 byte section
static const byte data[] = {
kDeclWLL, 0xfa, 0xff, 0xff, 0xff, 0x0f, // LEB128 0xfffffffa
1, 2, 3, 4, // 4 byte section
};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, ImportTable_empty) {
static const byte data[] = {kDeclSignatures, 0, kDeclImportTable, 0};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ImportTable_nosigs) {
static const byte data[] = {kDeclImportTable, 0};
EXPECT_FAILURE(data);
}
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
};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, ImportTable_one_sig) {
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
};
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
};
for (size_t length = 5; 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
......@@ -131,15 +131,25 @@ TEST_F(WasmMacroGenTest, Expressions) {
EXPECT_SIZE(6, WASM_LOOP(3, WASM_NOP, WASM_NOP, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, FunctionCalls) {
TEST_F(WasmMacroGenTest, CallFunction) {
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(0));
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(1));
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(11));
EXPECT_SIZE(4, WASM_CALL_FUNCTION(0, WASM_ZERO));
EXPECT_SIZE(6, WASM_CALL_FUNCTION(1, WASM_ZERO, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, CallImport) {
EXPECT_SIZE(2, WASM_CALL_IMPORT0(0));
EXPECT_SIZE(2, WASM_CALL_IMPORT0(1));
EXPECT_SIZE(2, WASM_CALL_IMPORT0(11));
EXPECT_SIZE(4, WASM_CALL_IMPORT(0, WASM_ZERO));
EXPECT_SIZE(6, WASM_CALL_IMPORT(1, WASM_ZERO, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, CallIndirect) {
EXPECT_SIZE(4, WASM_CALL_INDIRECT0(0, WASM_ZERO));
EXPECT_SIZE(4, WASM_CALL_INDIRECT0(1, WASM_ZERO));
EXPECT_SIZE(4, WASM_CALL_INDIRECT0(11, WASM_ZERO));
......
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