mirror of
				https://github.com/tc39/test262.git
				synced 2025-10-29 02:33:50 +01: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;
 | |
|     }
 | |
| }
 |