// 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-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); })();