| import Builder from '../Builder.js'; |
| import * as assert from '../assert.js'; |
| |
| const pageSize = 64 * 1024; |
| const maxPageCount = (2**32) / pageSize; |
| |
| function binaryShouldNotParse(builder) { |
| const bin = builder.WebAssembly().get(); |
| let threw = false; |
| try { |
| const module = new WebAssembly.Module(bin); |
| } catch(e) { |
| threw = true; |
| } |
| assert.truthy(threw); |
| } |
| |
| { |
| // Can't declare more than one memory. |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", {initial: 20}).End() |
| .Function().End() |
| .Memory().InitialMaxPages(1, 1).End() |
| .Export().End() |
| .Code() |
| .End(); |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| // Can't declare more than one memory. |
| const builder = (new Builder()) |
| .Type().End() |
| .Import() |
| .Memory("imp", "memory", {initial: 20}) |
| .Memory("imp", "memory", {initial: 30}) |
| .End() |
| .Function().End() |
| .Export().End() |
| .Code() |
| .End(); |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| // initial must be <= maximum. |
| const builder = (new Builder()) |
| .Type().End() |
| .Import() |
| .Memory("imp", "memory", {initial: 20, maximum: 19}) |
| .End() |
| .Function().End() |
| .Export().End() |
| .Code() |
| .End(); |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| // No loads when no memory defined. |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().End() |
| .Function().End() |
| .Export().Function("foo").End() |
| .Code() |
| .Function("foo", { params: ["i32"], ret: "i32" }) |
| .GetLocal(0) |
| .I32Load(2, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| // No stores when no memory defined. |
| const builder = (new Builder()) |
| .Type().End() |
| .Import() |
| .End() |
| .Function().End() |
| .Export().Function("foo").End() |
| .Code() |
| .Function("foo", { params: ["i32"] }) |
| .GetLocal(0) |
| .GetLocal(0) |
| .I32Store(2, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| // Can't export an undefined memory. |
| const builder = (new Builder()) |
| .Type().End() |
| .Function().End() |
| .Export() |
| .Memory("memory", 0) |
| .End() |
| .Code() |
| .End(); |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| // Can't export a non-zero memory index. |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", {initial: 20}).End() |
| .Function().End() |
| .Export() |
| .Memory("memory", 1) |
| .End() |
| .Code() |
| .End(); |
| binaryShouldNotParse(builder); |
| } |
| |
| { |
| assert.throws(() => new WebAssembly.Memory(20), TypeError, "WebAssembly.Memory expects its first argument to be an object"); |
| new WebAssembly.Memory({initial:1}, {}); |
| } |
| |
| function test(f) { |
| noInline(f); |
| for (let i = 0; i < 2; i++) { |
| f(i); |
| } |
| } |
| |
| test(function() { |
| const memoryDescription = {initial: 2, maximum: 2}; |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDescription).End() |
| .Function().End() |
| .Export() |
| .Function("foo") |
| .End() |
| .Code() |
| .Function("foo", { params: ["i32", "i32"], ret: "i32" }) |
| .GetLocal(1) |
| .GetLocal(0) |
| .I32Store(2, 0) |
| .GetLocal(1) |
| .I32Load(2, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| const memory = new WebAssembly.Memory(memoryDescription); |
| const instance = new WebAssembly.Instance(module, { imp: { memory: memory } }); |
| const foo = instance.exports.foo; |
| // foo(value, address) |
| |
| const bytes = memoryDescription.initial * pageSize; |
| for (let i = 0; i < (bytes/4); i++) { |
| let value = i + 1; |
| let address = i * 4; |
| let result = foo(value, address); |
| assert.truthy(result === value); |
| let arrayBuffer = memory.buffer; |
| let buffer = new Uint32Array(arrayBuffer); |
| assert.truthy(buffer[i] === value); |
| } |
| }); |
| |
| test(function() { |
| const memoryDescription = {initial: 2, maximum: 2}; |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDescription).End() |
| .Function().End() |
| .Export() |
| .Function("foo") |
| .End() |
| .Code() |
| .Function("foo", { params: ["i32", "i32"], ret: "i32"}) |
| .GetLocal(1) |
| .GetLocal(0) |
| .I32Store8(0, 0) |
| .GetLocal(1) |
| .I32Load8U(0, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| const memory = new WebAssembly.Memory(memoryDescription); |
| const instance = new WebAssembly.Instance(module, { imp: { memory: memory } }); |
| const foo = instance.exports.foo; |
| // foo(value, address) |
| |
| const bytes = memoryDescription.initial * pageSize; |
| for (let i = 0; i < bytes; i++) { |
| let value = (i + 1); |
| let address = i; |
| let result = foo(value, address); |
| let expectedValue = (value & ((2**8) - 1)); |
| assert.truthy(result === expectedValue); |
| let arrayBuffer = memory.buffer; |
| let buffer = new Uint8Array(arrayBuffer); |
| assert.truthy(buffer[i] === expectedValue); |
| } |
| }); |
| |
| test(function() { |
| const memoryDescription = {initial: 2, maximum: 2}; |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDescription).End() |
| .Function().End() |
| .Export() |
| .Function("foo") |
| .End() |
| .Code() |
| .Function("foo", { params: ["f32", "i32"], ret: "f32"}) |
| .GetLocal(1) |
| .GetLocal(0) |
| .F32Store(2, 0) |
| .GetLocal(1) |
| .F32Load(2, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| const memory = new WebAssembly.Memory(memoryDescription); |
| const instance = new WebAssembly.Instance(module, { imp: { memory: memory } }); |
| const foo = instance.exports.foo; |
| // foo(value, address) |
| |
| const bytes = memoryDescription.initial * pageSize; |
| for (let i = 0; i < (bytes/4); i++) { |
| let value = i + 1 + .0128213781289; |
| assert.truthy(value !== Math.fround(value)); |
| let address = i * 4; |
| let result = foo(value, address); |
| let expectedValue = Math.fround(result); |
| assert.truthy(result === expectedValue); |
| let arrayBuffer = memory.buffer; |
| let buffer = new Float32Array(arrayBuffer); |
| assert.truthy(buffer[i] === expectedValue); |
| } |
| }); |
| |
| test(function() { |
| const memoryDescription = {initial: 2, maximum: 2}; |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDescription).End() |
| .Function().End() |
| .Export() |
| .Function("foo") |
| .End() |
| .Code() |
| .Function("foo", { params: ["f64", "i32"], ret: "f64"}) |
| .GetLocal(1) |
| .GetLocal(0) |
| .F64Store(3, 0) |
| .GetLocal(1) |
| .F64Load(3, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| const memory = new WebAssembly.Memory(memoryDescription); |
| const instance = new WebAssembly.Instance(module, { imp: { memory: memory } }); |
| const foo = instance.exports.foo; |
| // foo(value, address) |
| |
| const bytes = memoryDescription.initial * pageSize; |
| for (let i = 0; i < (bytes/8); i++) { |
| let value = i + 1 + .0128213781289; |
| let address = i * 8; |
| let result = foo(value, address); |
| let expectedValue = result; |
| assert.truthy(result === expectedValue); |
| let arrayBuffer = memory.buffer; |
| let buffer = new Float64Array(arrayBuffer); |
| assert.truthy(buffer[i] === expectedValue); |
| } |
| }); |
| |
| test(function() { |
| const memoryDescription = {initial: 20, maximum: 25}; |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDescription).End() |
| .Function().End() |
| .Export() |
| .Function("foo") |
| .End() |
| .Code() |
| .Function("foo", { params: ["f64", "i32"], ret: "f64"}) |
| .GetLocal(1) |
| .GetLocal(0) |
| .F64Store(3, 0) |
| .GetLocal(1) |
| .F64Load(3, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| |
| assert.throws(() => new WebAssembly.Instance(module, 20), TypeError, `second argument to WebAssembly.Instance must be undefined or an Object`); |
| assert.throws(() => new WebAssembly.Instance(module, {}), TypeError, `import imp:memory must be an object`); |
| assert.throws(() => new WebAssembly.Instance(module, {imp: { } }), WebAssembly.LinkError, `Memory import imp:memory is not an instance of WebAssembly.Memory`); |
| assert.throws(() => new WebAssembly.Instance(module, {imp: { memory: 20 } }), WebAssembly.LinkError, `Memory import imp:memory is not an instance of WebAssembly.Memory`); |
| assert.throws(() => new WebAssembly.Instance(module, {imp: { memory: [] } }), WebAssembly.LinkError, `Memory import imp:memory is not an instance of WebAssembly.Memory`); |
| assert.throws(() => new WebAssembly.Instance(module, {imp: { memory: new WebAssembly.Memory({initial: 19, maximum: 25}) } }), WebAssembly.LinkError, `Memory import imp:memory provided an 'initial' that is smaller than the module's declared 'initial' import memory size`); |
| assert.throws(() => new WebAssembly.Instance(module, {imp: { memory: new WebAssembly.Memory({initial: 20}) } }), WebAssembly.LinkError, `Memory import imp:memory did not have a 'maximum' but the module requires that it does`); |
| assert.throws(() => new WebAssembly.Instance(module, {imp: { memory: new WebAssembly.Memory({initial: 20, maximum: 26}) } }), WebAssembly.LinkError, `Memory import imp:memory provided a 'maximum' that is larger than the module's declared 'maximum' import memory size`); |
| }); |
| |
| test(function() { |
| const memoryDescription = {initial: 20}; |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDescription).End() |
| .Function().End() |
| .Export() |
| .Function("foo") |
| .End() |
| .Code() |
| .Function("foo", { params: ["f64", "i32"], ret: "f64"}) |
| .GetLocal(1) |
| .GetLocal(0) |
| .F64Store(3, 0) |
| .GetLocal(1) |
| .F64Load(3, 0) |
| .Return() |
| .End() |
| .End(); |
| |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| |
| function testMemImportError(instanceObj, expectedError) { |
| assert.throws(() => new WebAssembly.Instance(module, instanceObj), WebAssembly.LinkError, expectedError); |
| } |
| |
| testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 19, maximum: 25}) } }, `Memory import imp:memory provided an 'initial' that is smaller than the module's declared 'initial' import memory size`); |
| testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 19}) } }, `Memory import imp:memory provided an 'initial' that is smaller than the module's declared 'initial' import memory size`); |
| |
| // This should not throw. |
| new WebAssembly.Instance(module, {imp: {memory: new WebAssembly.Memory({initial:20})}}); |
| new WebAssembly.Instance(module, {imp: {memory: new WebAssembly.Memory({initial:20, maximum:20})}}); |
| }); |
| |
| { |
| const builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", {initial: 20}).End() |
| .Function().End() |
| .Export() |
| .Memory("memory", 0) |
| .Memory("memory2", 0) |
| .End() |
| .Code() |
| .End(); |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| const memory = new WebAssembly.Memory({initial: 20}); |
| const instance = new WebAssembly.Instance(module, {imp: {memory}}); |
| assert.truthy(memory === instance.exports.memory); |
| assert.truthy(memory === instance.exports.memory2); |
| } |
| |
| { |
| const builder = (new Builder()) |
| .Type().End() |
| .Function().End() |
| .Memory().InitialMaxPages(20, 25).End() |
| .Export() |
| .Memory("memory", 0) |
| .Memory("memory2", 0) |
| .End() |
| .Code() |
| .End(); |
| const bin = builder.WebAssembly().get(); |
| const module = new WebAssembly.Module(bin); |
| const instance = new WebAssembly.Instance(module); |
| assert.eq(instance.exports.memory, instance.exports.memory2); |
| assert.eq(instance.exports.memory.buffer.byteLength, 20 * pageSize); |
| assert.truthy(instance.exports.memory instanceof WebAssembly.Memory); |
| } |
| |
| test(function() { |
| assert.throws(() => { |
| const m = new WebAssembly.Memory({minimum:40, maximum: 100}); |
| m.type.call({}); |
| }, TypeError, "WebAssembly.Memory.prototype.buffer getter called with non WebAssembly.Memory |this| value"); |
| |
| const memory = new WebAssembly.Memory({initial: 20}); |
| assert.eq(Object.keys(memory.type()).length, 2); |
| assert.eq(memory.type().minimum, 20); |
| assert.eq(memory.type().shared, false); |
| |
| const memory2 = new WebAssembly.Memory({minimum:40, maximum: 100}); |
| assert.eq(Object.keys(memory2.type()).length, 3); |
| assert.eq(memory2.type().minimum, 40); |
| assert.eq(memory2.type().maximum, 100); |
| assert.eq(memory.type().shared, false); |
| |
| const memory3 = new WebAssembly.Memory(memory2.type()); |
| assert.eq(Object.keys(memory2.type()).length, Object.keys(memory3.type()).length); |
| assert.eq(memory2.type().minimum, memory3.type().minimum); |
| assert.eq(memory2.type().maximum, memory3.type().maximum); |
| assert.eq(memory.type().shared, false); |
| |
| const memory4 = new WebAssembly.Memory({minimum: 10, maximum: 20, shared:true}); |
| assert.eq(Object.keys(memory4.type()).length, 3); |
| assert.eq(memory4.type().minimum, 10); |
| assert.eq(memory4.type().maximum, 20); |
| assert.eq(memory4.type().shared, true); |
| }) |