blob: 2af5888daa50e83cbd5b3987adad96f802eddc1b [file] [log] [blame]
// Copyright 2017 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');
load('test/mjsunit/wasm/wasm-module-builder.js');
let initialMemoryPages = 1;
let maximumMemoryPages = 5;
let other_fn_idx = 0;
// This builder can be used to generate a module with memory + load/store
// functions and/or an additional imported function.
function generateBuilder(add_memory, import_sig) {
let builder = new WasmModuleBuilder();
if (import_sig) {
// Add the import if we expect a module builder with imported functions.
let idx = builder.addImport('import_module', 'other_module_fn', import_sig);
// The imported function should always have index 0. With this assertion we
// verify that we can use other_fn_idx to refer to this function.
assertEquals(idx, other_fn_idx)
}
if (add_memory) {
// Add the memory if we expect a module builder with memory and load/store.
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
builder.addFunction('load', kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0])
.exportFunc();
builder.addFunction('store', kSig_i_ii)
.addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0,
kExprGetLocal, 1
])
.exportFunc();
}
return builder;
}
function assertMemoryIndependence(load_a, store_a, load_b, store_b) {
assertEquals(0, load_a(0));
assertEquals(0, load_b(0));
assertEquals(0, load_a(4));
assertEquals(0, load_b(4));
store_a(0, 101);
assertEquals(101, load_a(0));
assertEquals(0, load_b(0));
assertEquals(0, load_a(4));
assertEquals(0, load_b(4));
store_a(4, 102);
assertEquals(101, load_a(0));
assertEquals(0, load_b(0));
assertEquals(102, load_a(4));
assertEquals(0, load_b(4));
store_b(0, 103);
assertEquals(101, load_a(0));
assertEquals(103, load_b(0));
assertEquals(102, load_a(4));
assertEquals(0, load_b(4));
store_b(4, 107);
assertEquals(101, load_a(0));
assertEquals(103, load_b(0));
assertEquals(102, load_a(4));
assertEquals(107, load_b(4));
store_a(0, 0);
store_a(4, 0);
store_b(0, 0);
store_b(4, 0);
}
// A simple test for memory-independence between modules.
(function SimpleMemoryIndependenceTest() {
print("SimpleMemoryIndependenceTest");
let kPages = 1;
let builder = new WasmModuleBuilder();
builder.addMemory(kPages, kPages, true);
builder.addFunction("store", kSig_v_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32StoreMem, 0, 0, // --
]) // --
.exportFunc();
builder.addFunction("load", kSig_i_i)
.addBody([
kExprGetLocal, 0, // --
kExprI32LoadMem, 0, 0, // --
]) // --
.exportFunc();
var a = builder.instantiate();
// The {b} instance forwards all {store} calls to the imported function.
builder = new WasmModuleBuilder();
builder.addImport("mod", "store", kSig_v_ii);
builder.addMemory(kPages, kPages, true);
builder.addFunction("store", kSig_v_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallFunction, 0, // --
]) // --
.exportFunc();
builder.addFunction("load", kSig_i_i)
.addBody([
kExprGetLocal, 0, // --
kExprI32LoadMem, 0, 0, // --
]) // --
.exportFunc();
var b = builder.instantiate({mod: {store: a.exports.store}});
assertEquals(0, a.exports.load(0));
assertEquals(0, b.exports.load(0));
assertEquals(0, a.exports.load(4));
assertEquals(0, b.exports.load(4));
a.exports.store(0, 101);
assertEquals(101, a.exports.load(0));
assertEquals(0, b.exports.load(0));
assertEquals(0, a.exports.load(4));
assertEquals(0, b.exports.load(4));
a.exports.store(4, 102);
assertEquals(101, a.exports.load(0));
assertEquals(0, b.exports.load(0));
assertEquals(102, a.exports.load(4));
assertEquals(0, b.exports.load(4));
b.exports.store(4, 107); // should forward to {a}.
assertEquals(101, a.exports.load(0));
assertEquals(0, b.exports.load(0));
assertEquals(107, a.exports.load(4));
assertEquals(0, b.exports.load(4));
})();
// This test verifies that when a Wasm module without memory invokes a function
// imported from another module that has memory, the second module reads its own
// memory and returns the expected value.
(function TestExternalCallBetweenTwoWasmModulesWithoutAndWithMemory() {
print('TestExternalCallBetweenTwoWasmModulesWithoutAndWithMemory');
let first_module = generateBuilder(add_memory = false, import_sig = kSig_i_i);
// Function to invoke the imported function and add 1 to the result.
first_module.addFunction('plus_one', kSig_i_i)
.addBody([
kExprGetLocal, 0, // -
kExprCallFunction, other_fn_idx, // call the imported function
kExprI32Const, 1, // -
kExprI32Add, // add 1 to the result
kExprReturn // -
])
.exportFunc();
let second_module =
generateBuilder(add_memory = true, import_sig = undefined);
let index = kPageSize - 4;
let second_value = 2222;
// Instantiate the instances.
let second_instance = second_module.instantiate();
let first_instance = first_module.instantiate(
{import_module: {other_module_fn: second_instance.exports.load}});
// Write the values in the second instance.
second_instance.exports.store(index, second_value);
assertEquals(second_value, second_instance.exports.load(index));
// Verify that the value is correct when passing from the imported function.
assertEquals(second_value + 1, first_instance.exports.plus_one(index));
})();
// This test verifies that when a Wasm module with memory invokes a function
// imported from another module that also has memory, the second module reads
// its own memory and returns the expected value.
(function TestExternalCallBetweenTwoWasmModulesWithMemory() {
print('TestExternalCallBetweenTwoWasmModulesWithMemory');
let first_module = generateBuilder(add_memory = true, import_sig = kSig_i_i);
// Function to invoke the imported function and add 1 to the result.
first_module.addFunction('plus_one', kSig_i_i)
.addBody([
kExprGetLocal, 0, // -
kExprCallFunction, other_fn_idx, // call the imported function
kExprI32Const, 1, // -
kExprI32Add, // add 1 to the result
kExprReturn // -
])
.exportFunc();
let second_module =
generateBuilder(add_memory = true, import_sig = undefined);
let index = kPageSize - 4;
let first_value = 1111;
let second_value = 2222;
// Instantiate the instances.
let second_instance = second_module.instantiate();
let first_instance = first_module.instantiate(
{import_module: {other_module_fn: second_instance.exports.load}});
// Write the values in the two instances.
first_instance.exports.store(index, first_value);
second_instance.exports.store(index, second_value);
// Verify that the values were stored to memory.
assertEquals(first_value, first_instance.exports.load(index));
assertEquals(second_value, second_instance.exports.load(index));
// Verify that the value is correct when passing from the imported function.
assertEquals(second_value + 1, first_instance.exports.plus_one(index));
})();
// This test verifies that the correct memory is accessed after returning
// from a function imported from another module that also has memory.
(function TestCorrectMemoryAccessedAfterReturningFromExternalCall() {
print('TestCorrectMemoryAccessedAfterReturningFromExternalCall');
let first_module = generateBuilder(add_memory = true, import_sig = kSig_i_ii);
// Function to invoke the imported function and add 1 to the result.
first_module.addFunction('sandwich', kSig_i_iii)
.addBody([
kExprGetLocal, 0, // param0 (index)
kExprGetLocal, 1, // param1 (first_value)
kExprI32StoreMem, 0, 0, // store value in first_instance
kExprGetLocal, 0, // param0 (index)
kExprGetLocal, 2, // param2 (second_value)
kExprCallFunction, other_fn_idx, // call the imported function
kExprDrop, // drop the return value
kExprGetLocal, 0, // param0 (index)
kExprI32LoadMem, 0, 0, // load from first_instance
kExprReturn // -
])
.exportFunc();
let second_module =
generateBuilder(add_memory = true, import_sig = undefined);
let index = kPageSize - 4;
let first_value = 1111;
let second_value = 2222;
// Instantiate the instances.
let second_instance = second_module.instantiate();
let first_instance = first_module.instantiate(
{import_module: {other_module_fn: second_instance.exports.store}});
// Call the sandwich function and check that it returns the correct value.
assertEquals(
first_value,
first_instance.exports.sandwich(index, first_value, second_value));
// Verify that the values are correct in both memories.
assertEquals(first_value, first_instance.exports.load(index));
assertEquals(second_value, second_instance.exports.load(index));
})();
// A test for memory-independence between modules when calling through
// imported tables.
(function CallThroughTableMemoryIndependenceTest() {
print("CallThroughTableIndependenceTest");
let kTableSize = 2;
let kPages = 1;
let builder = new WasmModuleBuilder();
builder.addMemory(kPages, kPages, true);
builder.addFunction("store", kSig_v_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32StoreMem, 0, 0, // --
]) // --
.exportFunc();
builder.addFunction("load", kSig_i_i)
.addBody([
kExprGetLocal, 0, // --
kExprI32LoadMem, 0, 0, // --
]) // --
.exportFunc();
{
// Create two instances.
let module = builder.toModule();
var a = new WebAssembly.Instance(module);
var b = new WebAssembly.Instance(module);
// Check that the memories are initially independent.
assertMemoryIndependence(a.exports.load, a.exports.store,
b.exports.load, b.exports.store);
}
let table = new WebAssembly.Table({element: "anyfunc",
initial: kTableSize,
maximum: kTableSize});
table.set(0, a.exports.store);
table.set(1, b.exports.store);
// Check that calling (from JS) through the table maintains independence.
assertMemoryIndependence(a.exports.load, table.get(0),
b.exports.load, table.get(1));
table.set(1, a.exports.store);
table.set(0, b.exports.store);
// Check that calling (from JS) through the table maintains independence,
// even after reorganizing the table.
assertMemoryIndependence(a.exports.load, table.get(1),
b.exports.load, table.get(0));
// Check that calling (from WASM) through the table maintains independence.
builder = new WasmModuleBuilder();
builder.addImportedTable("m", "table", kTableSize, kTableSize);
var sig_index = builder.addType(kSig_v_ii);
builder.addFunction("store", kSig_v_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprCallIndirect, sig_index, kTableZero,
]).exportFunc();
let c = builder.instantiate({m: {table: table}});
let a_index = 1;
let b_index = 0;
let store_a = (index, val) => c.exports.store(a_index, index, val)
let store_b = (index, val) => c.exports.store(b_index, index, val);
assertMemoryIndependence(a.exports.load, store_a,
b.exports.load, store_b);
// Flip the order in the table and do it again.
table.set(0, a.exports.store);
table.set(1, b.exports.store);
a_index = 0;
b_index = 1;
assertMemoryIndependence(a.exports.load, store_a,
b.exports.load, store_b);
})();