mirror of
https://github.com/tc39/test262.git
synced 2025-05-04 06:50:32 +02:00
* [javascriptcore-test262-automation] changes from git@github.com:WebKit/webkit.git at sha 949e26452cfa153a7f4afe593da97e2fe9e1b706 on Tue Jul 03 2018 14:35:15 GMT-0400 (Eastern Daylight Time)
302 lines
15 KiB
JavaScript
302 lines
15 KiB
JavaScript
import * as assert from '../assert.js';
|
|
import Builder from '../Builder.js';
|
|
|
|
// FIXME: add the following: https://bugs.webkit.org/show_bug.cgi?id=171936
|
|
// - add set() and shadow memory, this requires tracking when memory is shared
|
|
// - Support: empty, exported
|
|
// - Imported memory created through the JS API (both before and after instantiation, to cause recompilation)
|
|
// - recursive calls (randomly call other instance's exports, potentially exhausting stack)
|
|
// - Simplify code by allowing .Code().ExportFunction(...) in builder
|
|
|
|
const tune = {
|
|
numRandomIterations: 64,
|
|
numRandomInvokes: 4,
|
|
maxInitial: 8,
|
|
maxMax: 8,
|
|
maxGrow: 8,
|
|
memoryGetCount: 1024,
|
|
};
|
|
|
|
const pageSize = 64 * 1024; // As determined by the WebAssembly specification.
|
|
|
|
let logString = "";
|
|
let log = what => logString += '\n' + what;
|
|
|
|
let instances = [];
|
|
|
|
const insert = (builder, importObject = undefined) => {
|
|
const location = (Math.random() * instances.length) | 0;
|
|
instances.splice(location, 0, new WebAssembly.Instance(new WebAssembly.Module(builder.WebAssembly().get()), importObject));
|
|
}
|
|
const initialMaxObject = hasMax => {
|
|
const initial = (Math.random() * tune.maxInitial) | 0;
|
|
if (!hasMax)
|
|
return { initial: initial, max: undefined };
|
|
const max = initial + ((Math.random() * tune.maxMax) | 0);
|
|
return { initial: initial, max: max };
|
|
};
|
|
const initialMaxObjectFrom = instance => {
|
|
const hasMax = instance.exports["max"] !== undefined;
|
|
const initial = (Math.random() * instance.exports.initial()) | 0;
|
|
if (!hasMax)
|
|
return { initial: initial };
|
|
const max = Math.max(initial, instance.exports.max()) + ((Math.random() * tune.maxMax) | 0);
|
|
return { initial: initial, max: max };
|
|
};
|
|
|
|
const action = {
|
|
'- delete': () => {
|
|
for (let count = 1 + (Math.random() * instances.length) | 0; instances.length && count; --count) {
|
|
const which = (Math.random() * instances.length) | 0;
|
|
log(` delete ${which} / ${instances.length}`);
|
|
instances.splice(which, 1);
|
|
}
|
|
},
|
|
'^ invoke': () => {
|
|
if (!instances.length) {
|
|
log(` nothing to invoke`);
|
|
return;
|
|
}
|
|
for (let iterations = 0; iterations < tune.numRandomInvokes; ++iterations) {
|
|
const whichInstance = (Math.random() * instances.length) | 0;
|
|
const instance = instances[whichInstance];
|
|
const whichExport = (Math.random() * Object.keys(instance.exports).length) | 0;
|
|
log(` instances[${whichInstance}].${Object.keys(instance.exports)[whichExport]}()`);
|
|
switch (Object.keys(instance.exports)[whichExport]) {
|
|
default:
|
|
throw new Error(`Implementation problem for key '${Object.keys(instance.exports)[whichExport]}'`);
|
|
case 'func':
|
|
assert.eq(instance.exports.func(), 42);
|
|
break;
|
|
case 'throws': {
|
|
let threw = false;
|
|
let address = 0;
|
|
if (instance.exports["current"]) {
|
|
// The instance has a memory.
|
|
const current = instance.exports.current();
|
|
const max = instance.exports["max"] ? instance.exports.max() : (1 << 15);
|
|
address = pageSize * (current + ((Math.random() * (max - current)) | 0));
|
|
}
|
|
// No memory in this instance.
|
|
try { instance.exports.throws(address); }
|
|
catch (e) { threw = true; }
|
|
assert.truthy(threw);
|
|
} break;
|
|
case 'get': {
|
|
const current = instance.exports.current();
|
|
for (let access = tune.memoryGetCount; current && access; --access) {
|
|
const address = (Math.random() * (current * pageSize - 4)) | 0;
|
|
assert.eq(instance.exports.get(address), 0);
|
|
}
|
|
} break;
|
|
case 'current':
|
|
assert.le(instance.exports.initial(), instance.exports.current());
|
|
break;
|
|
case 'initial':
|
|
assert.le(0, instance.exports.initial());
|
|
break;
|
|
case 'max':
|
|
assert.le(instance.exports.initial(), instance.exports.max());
|
|
assert.le(instance.exports.current(), instance.exports.max());
|
|
break;
|
|
case 'memory': {
|
|
const current = instance.exports.current();
|
|
const buffer = new Uint32Array(instance.exports.memory.buffer);
|
|
assert.eq(buffer.byteLength, current * pageSize);
|
|
for (let access = tune.memoryGetCount; current && access; --access) {
|
|
const address = (Math.random() * (current * pageSize - 4)) | 0;
|
|
assert.eq(instance.exports.get(address), 0);
|
|
}
|
|
} break;
|
|
case 'grow': {
|
|
const current = instance.exports.current();
|
|
const by = (Math.random() * tune.maxGrow) | 0;
|
|
const hasMax = instance.exports["max"] !== undefined;
|
|
const result = instance.exports.grow(current + by);
|
|
log(` Grow from ${current} (max ${hasMax ? instance.exports.max() : "none"}) to ${current + by} returned ${result}, current now ${instance.exports.current()}`);
|
|
assert.le(current, instance.exports.current());
|
|
if (result === -1)
|
|
assert.eq(current, instance.exports.current());
|
|
if (hasMax)
|
|
assert.le(instance.exports.current(), instance.exports.max());
|
|
} break;
|
|
}
|
|
}
|
|
},
|
|
'+ memory: none': () => {
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Function().End()
|
|
.Export().Function("func").Function("throws").End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).Unreachable().Return().End()
|
|
.End();
|
|
insert(builder);
|
|
},
|
|
'+ memory: empty, section': () => {
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Function().End()
|
|
.Memory().InitialMaxPages(0, 0).End()
|
|
.Export().Function("func").Function("throws").Function("current").Function("initial").Function("grow").Function("max").End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(0).Return().End()
|
|
.Function("max", { params: [], ret: "i32" }).I32Const(0).Return().End()
|
|
.End();
|
|
insert(builder);
|
|
},
|
|
'+ memory: section': () => {
|
|
const initialMax = initialMaxObject(false);
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Function().End()
|
|
.Memory().InitialMaxPages(initialMax.initial, initialMax.max).End()
|
|
.Export().Function("func").Function("throws").Function("get").Function("current").Function("initial").Function("grow").End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("get", { params: ["i32"], ret: "i32" }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(initialMax.initial).Return().End()
|
|
.End();
|
|
insert(builder);
|
|
},
|
|
'+ memory: section, max': () => {
|
|
const initialMax = initialMaxObject(true);
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Function().End()
|
|
.Memory().InitialMaxPages(initialMax.initial, initialMax.max).End()
|
|
.Export().Function("func").Function("throws").Function("get").Function("current").Function("initial").Function("grow").Function("max").End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("get", { params: ["i32"], ret: "i32" }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(initialMax.initial).Return().End()
|
|
.Function("max", { params: [], ret: "i32" }).I32Const(initialMax.max).Return().End()
|
|
.End();
|
|
insert(builder);
|
|
},
|
|
'+ memory: section, exported': () => {
|
|
const initialMax = initialMaxObject(false);
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Function().End()
|
|
.Memory().InitialMaxPages(initialMax.initial, initialMax.max).End()
|
|
.Export()
|
|
.Function("func").Function("throws").Function("get").Function("current").Function("initial")
|
|
.Memory("memory", 0)
|
|
.End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("get", { params: ["i32"], ret: "i32" }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(initialMax.initial).Return().End()
|
|
.End();
|
|
insert(builder);
|
|
},
|
|
'+ memory: section, exported, max': () => {
|
|
const initialMax = initialMaxObject(true);
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Function().End()
|
|
.Memory().InitialMaxPages(initialMax.initial, initialMax.max).End()
|
|
.Export()
|
|
.Function("func").Function("throws").Function("get").Function("current").Function("initial").Function("grow").Function("max")
|
|
.Memory("memory", 0)
|
|
.End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("get", { params: ["i32"], ret: "i32" }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(initialMax.initial).Return().End()
|
|
.Function("max", { params: [], ret: "i32" }).I32Const(initialMax.max).Return().End()
|
|
.End();
|
|
insert(builder);
|
|
},
|
|
'+ memory: imported, exported': () => {
|
|
let importFrom;
|
|
for (let idx = 0; idx < instances.length; ++idx)
|
|
if (instances[idx].exports.memory) {
|
|
importFrom = instances[idx];
|
|
break;
|
|
}
|
|
if (!importFrom)
|
|
return; // No memory could be imported.
|
|
const initialMax = initialMaxObjectFrom(importFrom);
|
|
if (importFrom.exports["max"] === undefined) {
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Import().Memory("imp", "memory", initialMax).End()
|
|
.Function().End()
|
|
.Export()
|
|
.Function("func").Function("throws").Function("get").Function("current").Function("initial")
|
|
.Memory("memory", 0)
|
|
.End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("get", { params: ["i32"], ret: "i32" }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(initialMax.initial).Return().End()
|
|
.End();
|
|
insert(builder, { imp: { memory: importFrom.exports.memory } });
|
|
} else {
|
|
const builder = (new Builder())
|
|
.Type().End()
|
|
.Import().Memory("imp", "memory", initialMax).End()
|
|
.Function().End()
|
|
.Export()
|
|
.Function("func").Function("throws").Function("get").Function("current").Function("initial").Function("grow").Function("max")
|
|
.Memory("memory", 0)
|
|
.End()
|
|
.Code()
|
|
.Function("func", { params: [], ret: "i32" }).I32Const(42).Return().End()
|
|
.Function("throws", { params: ["i32"] }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("get", { params: ["i32"], ret: "i32" }).GetLocal(0).I32Load(2, 0).Return().End()
|
|
.Function("current", { params: [], ret: "i32" }).CurrentMemory(0).Return().End()
|
|
.Function("grow", { params: ["i32"], ret: "i32" }).GetLocal(0).GrowMemory(0).Return().End()
|
|
.Function("initial", { params: [], ret: "i32" }).I32Const(initialMax.initial).Return().End()
|
|
.Function("max", { params: [], ret: "i32" }).I32Const(initialMax.max).Return().End()
|
|
.End();
|
|
insert(builder, { imp: { memory: importFrom.exports.memory } });
|
|
}
|
|
},
|
|
};
|
|
const numActions = Object.keys(action).length;
|
|
const performAction = () => {
|
|
const which = (Math.random() * numActions) | 0;
|
|
const key = Object.keys(action)[which];
|
|
log(`${key}:`);
|
|
action[key]();
|
|
};
|
|
|
|
try {
|
|
for (let iteration = 0; iteration < tune.numRandomIterations; ++iteration)
|
|
performAction();
|
|
log(`Finalizing:`);
|
|
// Finish by deleting the instances in a random order.
|
|
while (instances.length)
|
|
action["- delete"]();
|
|
} catch (e) {
|
|
// Ignore OOM while fuzzing. It can just happen.
|
|
if (e.message !== "Out of memory") {
|
|
print(`Failed: ${e}`);
|
|
print(logString);
|
|
throw e;
|
|
}
|
|
}
|