// 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-module-builder.js"); // V8 internal memory size limit. var kV8MaxPages = 65536; (function TestOne() { print("TestOne"); let memory = new WebAssembly.Memory({initial: 1}); assertEquals(kPageSize, memory.buffer.byteLength); let i32 = new Int32Array(memory.buffer); let builder = new WasmModuleBuilder(); builder.addImportedMemory("mod", "mine"); builder.addFunction("main", kSig_i_v) .addBody([ kExprI32Const, 0, kExprI32LoadMem, 0, 0]) .exportAs("main"); let main = builder.instantiate({mod: {mine: memory}}).exports.main; assertEquals(0, main()); i32[0] = 993377; assertEquals(993377, main()); })(); (function TestIdentity() { print("TestIdentity"); let memory = new WebAssembly.Memory({initial: 1}); let i32 = new Int32Array(memory.buffer); let builder = new WasmModuleBuilder(); builder.addImportedMemory("dad", "garg"); builder.exportMemoryAs("daggle"); let instance = builder.instantiate({dad: {garg: memory}}); assertSame(memory, instance.exports.daggle); })(); (function TestImportExport() { print("TestImportExport"); var i1; { let builder = new WasmModuleBuilder(); builder.addMemory(1, 1, false); builder.exportMemoryAs("exported_mem"); builder.addFunction("foo", kSig_i_i) .addBody([ kExprLocalGet, 0, kExprI32LoadMem, 0, 0]) .exportAs("foo"); i1 = builder.instantiate(); } var i2; { let builder = new WasmModuleBuilder(); builder.addImportedMemory("fil", "imported_mem"); builder.addFunction("bar", kSig_i_i) .addBody([ kExprLocalGet, 0, kExprI32LoadMem, 0, 0]) .exportAs("bar"); i2 = builder.instantiate({fil: {imported_mem: i1.exports.exported_mem}}); } let i32 = new Int32Array(i1.exports.exported_mem.buffer); for (var i = 0; i < 1e11; i = i * 3 + 5) { for (var j = 0; j < 10; j++) { var val = i + 99077 + j; i32[j] = val; assertEquals(val | 0, i1.exports.foo(j * 4)); assertEquals(val | 0, i2.exports.bar(j * 4)); } } })(); (function ValidateBoundsCheck() { print("ValidateBoundsCheck"); let memory = new WebAssembly.Memory({initial: 1, maximum: 5}); assertEquals(kPageSize, memory.buffer.byteLength); let i32 = new Int32Array(memory.buffer); let builder = new WasmModuleBuilder(); builder.addImportedMemory("gaz", "mine"); builder.addFunction("load", kSig_i_i) .addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0]) .exportFunc(); builder.addFunction("store", kSig_i_ii) .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0, kExprLocalGet, 1]) .exportFunc(); var offset; let instance = builder.instantiate({gaz: {mine: memory}}); function load() { return instance.exports.load(offset); } function store(value) { return instance.exports.store(offset, value); } for (offset = 0; offset < kPageSize - 3; offset+=4) { store(offset); } for (offset = 0; offset < kPageSize - 3; offset+=4) { assertEquals(offset, load()); } for (offset = kPageSize - 3; offset < kPageSize + 4; offset++) { assertTraps(kTrapMemOutOfBounds, load); } })(); (function TestMemoryGrowMaxDesc() { print("MaximumDescriptor"); let memory = new WebAssembly.Memory({initial: 1, maximum: 5}); assertEquals(kPageSize, memory.buffer.byteLength); let i32 = new Int32Array(memory.buffer); let builder = new WasmModuleBuilder(); builder.addImportedMemory("mine", "dog", 0, 20); builder.addFunction("load", kSig_i_i) .addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0]) .exportFunc(); builder.addFunction("store", kSig_i_ii) .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0, kExprLocalGet, 1]) .exportFunc(); var offset; let instance = builder.instantiate({mine: {dog: memory}}); function load() { return instance.exports.load(offset); } function store(value) { return instance.exports.store(offset, value); } for (var i = 1; i < 5; i++) { for (offset = (i - 1) * kPageSize; offset < i * kPageSize - 3; offset+=4) { store(offset * 2); } assertEquals(i, memory.grow(1)); assertEquals((i + 1) * kPageSize, memory.buffer.byteLength); } for (offset = 4 * kPageSize; offset < 5 * kPageSize - 3; offset+=4) { store(offset * 2); } for (offset = 0; offset < 5 * kPageSize - 3; offset+=4) { assertEquals(offset * 2, load()); } for (offset = 5 * kPageSize; offset < 5 * kPageSize + 4; offset++) { assertThrows(load); } assertThrows(() => memory.grow(1)); })(); (function TestMemoryGrowZeroInitialMemory() { print("ZeroInitialMemory"); let memory = new WebAssembly.Memory({initial: 0}); assertEquals(0, memory.buffer.byteLength); let i32 = new Int32Array(memory.buffer); let builder = new WasmModuleBuilder(); builder.addImportedMemory("mine", "fro"); builder.addFunction("load", kSig_i_i) .addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0]) .exportFunc(); builder.addFunction("store", kSig_i_ii) .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0, kExprLocalGet, 1]) .exportFunc(); var offset; let instance = builder.instantiate({mine: {fro: memory}}); function load() { return instance.exports.load(offset); } function store(value) { return instance.exports.store(offset, value); } for (var i = 1; i < 5; i++) { assertEquals(i - 1, memory.grow(1)); assertEquals(i * kPageSize, memory.buffer.byteLength); for (offset = (i - 1) * kPageSize; offset < i * kPageSize - 3; offset++) { store(offset * 2); } } for (offset = 5 * kPageSize; offset < 5 * kPageSize + 4; offset++) { assertThrows(load); } assertThrows(() => memory.grow(kV8MaxPages - 3)); })(); (function ImportedMemoryBufferLength() { print("ImportedMemoryBufferLength"); let memory = new WebAssembly.Memory({initial: 2, maximum: 10}); assertEquals(2*kPageSize, memory.buffer.byteLength); let builder = new WasmModuleBuilder(); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); builder.addImportedMemory("cat", "mine"); let instance = builder.instantiate({cat: {mine: memory}}); function grow(pages) { return instance.exports.grow(pages); } assertEquals(2, grow(3)); assertEquals(5*kPageSize, memory.buffer.byteLength); assertEquals(5, grow(5)); assertEquals(10*kPageSize, memory.buffer.byteLength); assertThrows(() => memory.grow(1)); })(); (function TestMemoryGrowExportedMaximum() { print("TestMemoryGrowExportedMaximum"); let initial_size = 1, maximum_size = 10; var exp_instance; { let builder = new WasmModuleBuilder(); builder.addMemory(initial_size, maximum_size, true); builder.exportMemoryAs("exported_mem"); exp_instance = builder.instantiate(); } var instance; { var builder = new WasmModuleBuilder(); builder.addImportedMemory("fur", "imported_mem"); builder.addFunction("mem_size", kSig_i_v) .addBody([kExprMemorySize, kMemoryZero]) .exportFunc(); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); instance = builder.instantiate({fur: { imported_mem: exp_instance.exports.exported_mem}}); } for (var i = initial_size; i < maximum_size; i++) { assertEquals(i, instance.exports.grow(1)); assertEquals((i+1), instance.exports.mem_size()); } assertEquals(-1, instance.exports.grow(1)); })(); (function TestMemoryGrowWebAssemblyInstances() { print("TestMemoryGrowWebAssemblyInstances"); let memory = new WebAssembly.Memory({initial: 1, maximum: 15}); var builder = new WasmModuleBuilder(); builder.addImportedMemory("lit", "imported_mem"); builder.addFunction("mem_size", kSig_i_v) .addBody([kExprMemorySize, kMemoryZero]) .exportAs("mem_size"); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); var module = new WebAssembly.Module(builder.toBuffer()); var instances = []; for (var i = 0; i < 6; i++) { instances.push(new WebAssembly.Instance(module, {lit: {imported_mem: memory}})); } function verify_mem_size(expected_pages) { assertEquals(expected_pages*kPageSize, memory.buffer.byteLength); for (var i = 0; i < 6; i++) { assertEquals(expected_pages, instances[i].exports.mem_size()); } } // Verify initial memory size verify_mem_size(1); // Verify memory size with interleaving calls to Memory.grow, // MemoryGrow opcode. var current_mem_size = 1; for (var i = 0; i < 5; i++) { function grow(pages) { return instances[i].exports.grow(pages); } assertEquals(current_mem_size, memory.grow(1)); verify_mem_size(++current_mem_size); assertEquals(current_mem_size, instances[i].exports.grow(1)); verify_mem_size(++current_mem_size); } assertThrows(() => memory.grow(5)); })(); (function TestImportedMemoryGrowMultipleInstances() { print("TestImportMemoryMultipleInstances"); let memory = new WebAssembly.Memory({initial: 5, maximum: 100}); var builder = new WasmModuleBuilder(); builder.addImportedMemory("nob", "imported_mem"); builder.addFunction("mem_size", kSig_i_v) .addBody([kExprMemorySize, kMemoryZero]) .exportFunc(); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); var instances = []; for (var i = 0; i < 5; i++) { instances.push(builder.instantiate({nob: {imported_mem: memory}})); } function grow_instance_0(pages) { return instances[0].exports.grow(pages); } function grow_instance_1(pages) { return instances[1].exports.grow(pages); } function grow_instance_2(pages) { return instances[2].exports.grow(pages); } function grow_instance_3(pages) { return instances[3].exports.grow(pages); } function grow_instance_4(pages) { return instances[4].exports.grow(pages); } function verify_mem_size(expected_pages) { assertEquals(expected_pages*kPageSize, memory.buffer.byteLength); for (var i = 0; i < 5; i++) { assertEquals(expected_pages, instances[i].exports.mem_size()); } } // Verify initial memory size verify_mem_size(5); // Grow instance memory and buffer memory out of order and verify memory is // updated correctly. assertEquals(5, grow_instance_0(7)); verify_mem_size(12); assertEquals(12, memory.grow(4)); verify_mem_size(16); assertEquals(16, grow_instance_4(1)); verify_mem_size(17); assertEquals(17, grow_instance_1(6)); verify_mem_size(23); assertEquals(23, grow_instance_3(2)); verify_mem_size(25); assertEquals(25, memory.grow(10)); verify_mem_size(35); assertEquals(35, grow_instance_2(15)); verify_mem_size(50); assertThrows(() => memory.grow(51)); })(); (function TestExportImportedMemoryGrowMultipleInstances() { print("TestExportImportedMemoryGrowMultipleInstances"); var instance; { let builder = new WasmModuleBuilder(); builder.addMemory(1, 11, true); builder.exportMemoryAs("exported_mem"); builder.addFunction("mem_size", kSig_i_v) .addBody([kExprMemorySize, kMemoryZero]) .exportFunc(); instance = builder.instantiate(); } var builder = new WasmModuleBuilder(); builder.addImportedMemory("doo", "imported_mem"); builder.addFunction("mem_size", kSig_i_v) .addBody([kExprMemorySize, kMemoryZero]) .exportFunc(); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); var instances = []; for (var i = 0; i < 10; i++) { instances.push(builder.instantiate({ doo: {imported_mem: instance.exports.exported_mem}})); } function verify_mem_size(expected_pages) { for (var i = 0; i < 10; i++) { assertEquals(expected_pages, instances[i].exports.mem_size()); } } var current_mem_size = 1; for (var i = 0; i < 10; i++) { function grow(pages) { return instances[i].exports.grow(pages); } assertEquals(current_mem_size, instances[i].exports.grow(1)); verify_mem_size(++current_mem_size); } for (var i = 0; i < 10; i++) { assertEquals(-1, instances[i].exports.grow(1)); verify_mem_size(current_mem_size); } })(); (function TestExportImportedMemoryGrowPastV8Maximum() { // The spec maximum is higher than the internal V8 maximum. This test only // checks that grow_memory does not grow past the internally defined maximum // to reflect the current implementation even when the memory is exported. print("TestExportImportedMemoryGrowPastV8Maximum"); var instance_1, instance_2; { let builder = new WasmModuleBuilder(); builder.addMemory(1, kSpecMaxPages, true); builder.exportMemoryAs("exported_mem"); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); instance_1 = builder.instantiate(); } { let builder = new WasmModuleBuilder(); builder.addImportedMemory("doo", "imported_mem"); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); instance_2 = builder.instantiate({ doo: {imported_mem: instance_1.exports.exported_mem}}); } assertEquals(1, instance_1.exports.grow(20)); assertEquals(21, instance_2.exports.grow(20)); assertEquals(-1, instance_1.exports.grow(kV8MaxPages - 40)); assertEquals(-1, instance_2.exports.grow(kV8MaxPages - 40)); })(); (function TestExportGrow() { print("TestExportGrow"); let builder = new WasmModuleBuilder(); builder.addMemory(1, 5, true); builder.exportMemoryAs("exported_mem"); builder.addFunction("mem_size", kSig_i_v) .addBody([kExprMemorySize, kMemoryZero]) .exportFunc(); builder.addFunction("grow", kSig_i_i) .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero]) .exportFunc(); instance = builder.instantiate(); assertEquals(kPageSize, instance.exports.exported_mem.buffer.byteLength); assertEquals(1, instance.exports.grow(2)); assertEquals(3, instance.exports.mem_size()); assertEquals(3*kPageSize, instance.exports.exported_mem.buffer.byteLength); })(); (function TestImportTooLarge() { print("TestImportTooLarge"); let builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "m", 1, 2); // initial size is too large assertThrows(() => builder.instantiate({m: {m: new WebAssembly.Memory({initial: 3, maximum: 3})}})); // maximum size is too large assertThrows(() => builder.instantiate({m: {m: new WebAssembly.Memory({initial: 1, maximum: 4})}})); // no maximum assertThrows(() => builder.instantiate({m: {m: new WebAssembly.Memory({initial: 1})}})); })(); (function TestMemoryGrowDetachBuffer() { print("TestMemoryGrowDetachBuffer"); let memory = new WebAssembly.Memory({initial: 1, maximum: 5}); var builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "imported_mem"); let instance = builder.instantiate({m: {imported_mem: memory}}); let buffer = memory.buffer; assertEquals(kPageSize, buffer.byteLength); assertEquals(1, memory.grow(2)); assertTrue(buffer !== memory.buffer); assertEquals(0, buffer.byteLength); assertEquals(3*kPageSize, memory.buffer.byteLength); })(); (function TestInitialMemorySharedModule() { print("TestInitialMemorySharedModule"); var builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "imported_mem"); builder.addFunction('f', kSig_i_v) .addBody([ kExprI32Const, 0x1d, // -- kExprI32Const, 0x20, // -- kExprI32StoreMem, 0, 0, // -- kExprI32Const, 0x1d, // -- kExprI32LoadMem, 0, 0, // -- ]) .exportFunc(); // First instance load/store success var module = new WebAssembly.Module(builder.toBuffer()); let memory1= new WebAssembly.Memory({initial: 1, maximum: 20}); let instance1 = new WebAssembly.Instance(module, {m: {imported_mem: memory1}}); assertEquals(0x20, instance1.exports.f()); // Second instance should trap as it has no initial memory let memory2= new WebAssembly.Memory({initial: 0, maximum: 2}); let instance2 = new WebAssembly.Instance(module, {m: {imported_mem: memory2}}); assertTraps(kTrapMemOutOfBounds, () => instance2.exports.f()); })();