blob: 9ea2b2bfb89f1da523dd1abb5badec448e00ca25 [file] [log] [blame]
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);
})