├── .babelrc ├── .eslintrc.json ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── tasks.json ├── README.md ├── cli.js ├── index.js ├── lib ├── build.js ├── cli.js ├── dependencies.js ├── generate.js ├── index.js ├── init.js └── utils.js ├── package.json ├── src ├── build.js ├── cli.js ├── dependencies.js ├── generate.js ├── index.js ├── init.js └── utils.js ├── third_party ├── m4.exe └── regex2.dll └── updateThirdParties.cmd /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest" 4 | ], 5 | "plugins": ["transform-object-rest-spread"] 6 | } 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "cellule/global.js", 4 | "cellule/node.js", 5 | "cellule/codestyle.js", 6 | "cellule/es2015.js", 7 | "cellule/babel.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tools 2 | output 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "csmith"] 2 | path = third_party/csmith 3 | url = https://github.com/csmith-project/csmith 4 | [submodule "emscripten-fastcomp"] 5 | path = third_party/emscripten-fastcomp 6 | url = https://github.com/kripken/emscripten-fastcomp 7 | [submodule "clang"] 8 | path = third_party/clang 9 | url = https://github.com/kripken/emscripten-fastcomp-clang 10 | [submodule "emscripten"] 11 | path = third_party/emscripten 12 | url = https://github.com/kripken/emscripten 13 | [submodule "wasm-spec"] 14 | path = third_party/wasm-spec 15 | url = https://github.com/WebAssembly/spec.git 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Generate test file", 11 | "program": "${workspaceRoot}\\cli.js", 12 | "cwd": "${workspaceRoot}", 13 | "sourceMaps": true, 14 | "preLaunchTask": "build", 15 | "outFiles": [ 16 | "${workspaceRoot}\\lib\\**" 17 | ], 18 | "stopOnEntry": false, 19 | "args": [ 20 | "gen", 21 | "--inline" 22 | ], 23 | "runtimeArgs": [ "--nolazy" ] 24 | }, 25 | { 26 | "type": "node", 27 | "request": "launch", 28 | "name": "Build tools", 29 | "program": "${workspaceRoot}\\cli.js", 30 | "cwd": "${workspaceRoot}", 31 | "sourceMaps": true, 32 | "preLaunchTask": "build", 33 | "outFiles": [ 34 | "${workspaceRoot}\\lib\\**" 35 | ], 36 | "stopOnEntry": false, 37 | "args": [ 38 | "build" 39 | ], 40 | "runtimeArgs": [ "--nolazy" ] 41 | }, 42 | { 43 | "type": "node", 44 | "request": "launch", 45 | "name": "Config", 46 | "program": "${workspaceRoot}\\cli.js", 47 | "cwd": "${workspaceRoot}", 48 | "sourceMaps": true, 49 | "preLaunchTask": "build", 50 | "outFiles": [ 51 | "${workspaceRoot}\\lib\\**" 52 | ], 53 | "stopOnEntry": false, 54 | "args": [ 55 | "config" 56 | ], 57 | "runtimeArgs": [ "--nolazy" ] 58 | }, 59 | { 60 | "type": "node", 61 | "request": "attach", 62 | "name": "Attach to Process", 63 | "port": 5858 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "npm", 6 | "isShellCommand": true, 7 | "showOutput": "always", 8 | "suppressTaskName": true, 9 | "tasks": [ 10 | { 11 | "taskName": "install", 12 | "args": ["install"] 13 | }, 14 | { 15 | "taskName": "update", 16 | "args": ["update"] 17 | }, 18 | { 19 | "taskName": "build", 20 | "args": ["run", "build", "--", "--source-maps"], 21 | "isBuildCommand": true 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wasm-Exprgen 2 | 3 | This is a tool to generate random WebAssembly programs using csmith and emscripten. 4 | 5 | ## Requirements 6 | 7 | - Install [node.js & npm](https://nodejs.org/en/) version 0.10.17 and above 8 | - Install [Python 2.x](https://www.python.org/download/releases/2.7/) 9 | - Install [WebAssembly Interpreter](https://github.com/WebAssembly/spec/tree/master/interpreter#building) compiler dependencies (Optional) 10 | - Windows 11 | - [Ocaml](https://protz.github.io/ocaml-installer/) 12 | - Linux 13 | - [Ocaml](https://wasm.storage.googleapis.com/ocaml-4.02.2.tar.gz) 14 | - Install Compiler 15 | - Windows 16 | - [Visual Studio 2010 or above](https://www.visualstudio.com/), tested on Visual Studio 2015 17 | - [cmake](https://cmake.org/download/) 18 | - Linux 19 | - gcc: `sudo apt-get install build-essential` 20 | - cmake: `sudo apt-get install cmake` 21 | - m4: `sudo apt-get install m4` 22 | 23 | For more details on how to build Emscripten from source refer to [Emscripten Toolchain requirements](http://kripken.github.io/emscripten-site/docs/building_from_source/toolchain_what_is_needed.html) 24 | 25 | ## Setup 26 | 27 | ```bash 28 | # Initialize submodule 29 | git submodule update --init 30 | 31 | # Install required modules for build scripts 32 | npm install --production 33 | ``` 34 | 35 | ## Building tools 36 | 37 | ```bash 38 | npm run build-tools 39 | ``` 40 | 41 | ## Usage 42 | 43 | The following command will run csmith to generate a .c file then it runs emscripten to compile and generate WebAssembly from it. 44 | It is possible for csmith to generate code that is not supported by llvm. 45 | 46 | ```bash 47 | npm run gen 48 | ``` 49 | 50 | At this point, if it successfully built in llvm, there should be a a series of `test.*` files in the `output` directory. 51 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | require("babel-polyfill"); 3 | require("./lib/cli"); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./lib/index"); -------------------------------------------------------------------------------- /lib/build.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.prepareEmscriptenConfig = undefined; 7 | 8 | var buildCSmith = function () { 9 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { 10 | var _ref2, msbuild, cmake, m4, csmithBuildDir, csmithPath, vcproj, output; 11 | 12 | return regeneratorRuntime.wrap(function _callee$(_context) { 13 | while (1) { 14 | switch (_context.prev = _context.next) { 15 | case 0: 16 | _context.next = 2; 17 | return (0, _dependencies.csmithDependencies)(); 18 | 19 | case 2: 20 | _ref2 = _context.sent; 21 | msbuild = _ref2.msbuild; 22 | cmake = _ref2.cmake; 23 | m4 = _ref2.m4; 24 | csmithBuildDir = _init.buildDirectory.csmith; 25 | _context.next = 9; 26 | return _fsExtra2.default.ensureDirAsync(csmithBuildDir); 27 | 28 | case 9: 29 | console.log("Starting CSmith build"); 30 | csmithPath = _init.thirdParties.csmith; 31 | _context.next = 13; 32 | return (0, _child_process.execFileAsync)(cmake, [csmithPath], { 33 | cwd: csmithBuildDir, 34 | env: { 35 | path: _path2.default.dirname(msbuild) + ";" + _path2.default.dirname(m4) + ";" + process.env.path 36 | } 37 | }); 38 | 39 | case 13: 40 | if (!msbuild) { 41 | _context.next = 19; 42 | break; 43 | } 44 | 45 | vcproj = _path2.default.join(csmithBuildDir, "csmith.sln"); 46 | _context.next = 17; 47 | return (0, _child_process.execFileAsync)(msbuild, [vcproj, "/p:Configuration=Release"]); 48 | 49 | case 17: 50 | output = _path2.default.join(_init.binDirectory.csmith, "csmith.exe"); 51 | 52 | console.log("CSmith output: " + output); 53 | 54 | case 19: 55 | case "end": 56 | return _context.stop(); 57 | } 58 | } 59 | }, _callee, this); 60 | })); 61 | 62 | return function buildCSmith() { 63 | return _ref.apply(this, arguments); 64 | }; 65 | }(); 66 | 67 | var buildLLVM = function () { 68 | var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { 69 | var clangPath, clangDestPath, _ref4, cmake, msbuild, make, buildDir, llvmPath, solution, proc, _proc; 70 | 71 | return regeneratorRuntime.wrap(function _callee2$(_context2) { 72 | while (1) { 73 | switch (_context2.prev = _context2.next) { 74 | case 0: 75 | clangPath = _init.thirdParties.clang; 76 | clangDestPath = _path2.default.join(_init.thirdParties.llvm, "tools/clang"); 77 | 78 | console.log("Copying " + clangPath + " => " + clangDestPath); 79 | _context2.next = 5; 80 | return _fsExtra2.default.copyAsync(clangPath, clangDestPath); 81 | 82 | case 5: 83 | _context2.next = 7; 84 | return (0, _dependencies.llvmDependencies)(); 85 | 86 | case 7: 87 | _ref4 = _context2.sent; 88 | cmake = _ref4.cmake; 89 | msbuild = _ref4.msbuild; 90 | make = _ref4.make; 91 | 92 | console.log("Starting LLVM build. This might take a while..."); 93 | 94 | buildDir = _init.buildDirectory.llvm; 95 | _context2.next = 15; 96 | return _fsExtra2.default.ensureDirAsync(buildDir); 97 | 98 | case 15: 99 | llvmPath = _init.thirdParties.llvm; 100 | 101 | console.log("Running cmake"); 102 | _context2.next = 19; 103 | return (0, _child_process.execFileAsync)(cmake, [llvmPath, "-DCMAKE_BUILD_TYPE=Release", "-DLLVM_TARGETS_TO_BUILD=X86;JSBackend", "-DLLVM_INCLUDE_EXAMPLES=OFF", "-DLLVM_INCLUDE_TESTS=OFF", "-DCLANG_INCLUDE_EXAMPLES=OFF", "-DCLANG_INCLUDE_TESTS=OFF"], { 104 | cwd: buildDir, 105 | env: { 106 | path: _path2.default.dirname(msbuild) + ";" + process.env.path 107 | } 108 | }); 109 | 110 | case 19: 111 | if (!msbuild) { 112 | _context2.next = 29; 113 | break; 114 | } 115 | 116 | _context2.next = 22; 117 | return new Promise(function (resolve, reject) { 118 | return (0, _klaw2.default)(buildDir).on("data", function (item) { 119 | if (_path2.default.extname(item.path) === ".vcxproj") { 120 | var content = _fsExtra2.default.readFileSync(item.path).toString().split("\n").filter(function (line) { 121 | return line.indexOf("nologo") === -1; 122 | }).join("\n"); 123 | _fsExtra2.default.writeFileSync(item.path, content); 124 | } 125 | }).on("end", resolve).on("error", reject); 126 | }); 127 | 128 | case 22: 129 | console.log("Running msbuild"); 130 | solution = _path2.default.join(buildDir, "LLVM.sln"); 131 | proc = (0, _child_process.spawn)(msbuild, [solution, "/p:Configuration=Release"], { 132 | stdio: "inherit" 133 | }); 134 | _context2.next = 27; 135 | return (0, _utils.waitUntilDone)(proc); 136 | 137 | case 27: 138 | _context2.next = 33; 139 | break; 140 | 141 | case 29: 142 | console.log("Running make"); 143 | _proc = (0, _child_process.spawn)(make, ["-j4"], { 144 | cwd: buildDir, 145 | stdio: "inherit" 146 | }); 147 | _context2.next = 33; 148 | return (0, _utils.waitUntilDone)(_proc); 149 | 150 | case 33: 151 | console.log("LLVM output: " + _init.binDirectory.llvm); 152 | 153 | case 34: 154 | case "end": 155 | return _context2.stop(); 156 | } 157 | } 158 | }, _callee2, this); 159 | })); 160 | 161 | return function buildLLVM() { 162 | return _ref3.apply(this, arguments); 163 | }; 164 | }(); 165 | 166 | var prepareEmscriptenConfig = exports.prepareEmscriptenConfig = function () { 167 | var _ref5 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { 168 | var _ref6, python, cleanPath, tmpDir, file, emscriptenFile; 169 | 170 | return regeneratorRuntime.wrap(function _callee3$(_context3) { 171 | while (1) { 172 | switch (_context3.prev = _context3.next) { 173 | case 0: 174 | _context3.next = 2; 175 | return (0, _dependencies.emscriptenDependencies)(); 176 | 177 | case 2: 178 | _ref6 = _context3.sent; 179 | python = _ref6.python; 180 | 181 | cleanPath = function cleanPath(p) { 182 | return p.replace(/\\/g, "/"); 183 | }; 184 | 185 | tmpDir = _path2.default.join(_init.outputDir, "tmp"); 186 | file = "\nEMSCRIPTEN_ROOT = '" + cleanPath(_init.thirdParties.emscripten) + "'\nLLVM_ROOT= '" + cleanPath(_init.binDirectory.llvm) + "'\nPYTHON = '" + cleanPath(python) + "'\nNODE_JS= '" + cleanPath(process.argv[0]) + "'\nTEMP_DIR = '" + cleanPath(tmpDir) + "'\nCOMPILER_ENGINE = NODE_JS\nJS_ENGINES = [NODE_JS]\n"; 187 | _context3.next = 9; 188 | return _fsExtra2.default.ensureDirAsync(tmpDir); 189 | 190 | case 9: 191 | emscriptenFile = _path2.default.join(_init.outputDir, ".emscripten"); 192 | _context3.next = 12; 193 | return _fsExtra2.default.outputFileAsync(emscriptenFile, file); 194 | 195 | case 12: 196 | console.log("Generated config file " + emscriptenFile); 197 | 198 | case 13: 199 | case "end": 200 | return _context3.stop(); 201 | } 202 | } 203 | }, _callee3, this); 204 | })); 205 | 206 | return function prepareEmscriptenConfig() { 207 | return _ref5.apply(this, arguments); 208 | }; 209 | }(); 210 | 211 | var buildSpecInterpreter = function () { 212 | var _ref7 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() { 213 | var interpreterPath, _ref8, cmd, make, proc, _proc2; 214 | 215 | return regeneratorRuntime.wrap(function _callee4$(_context4) { 216 | while (1) { 217 | switch (_context4.prev = _context4.next) { 218 | case 0: 219 | console.log("Starting Spec interpreter build"); 220 | _context4.next = 3; 221 | return _fsExtra2.default.ensureDirAsync(_init.buildDirectory.spec); 222 | 223 | case 3: 224 | interpreterPath = _path2.default.join(_init.thirdParties.spec, "interpreter"); 225 | _context4.next = 6; 226 | return (0, _dependencies.specInterpreterDependencies)(); 227 | 228 | case 6: 229 | _ref8 = _context4.sent; 230 | cmd = _ref8.cmd; 231 | make = _ref8.make; 232 | 233 | if (!cmd) { 234 | _context4.next = 17; 235 | break; 236 | } 237 | 238 | proc = (0, _child_process.spawn)(cmd, ["/c", "winmake"], { 239 | cwd: interpreterPath 240 | }); 241 | _context4.next = 13; 242 | return (0, _utils.waitUntilDone)(proc); 243 | 244 | case 13: 245 | _context4.next = 15; 246 | return _fsExtra2.default.copyAsync(_path2.default.join(interpreterPath, "main", "main.d.byte"), _path2.default.join(_init.buildDirectory.spec, "wasm.exe")); 247 | 248 | case 15: 249 | _context4.next = 22; 250 | break; 251 | 252 | case 17: 253 | _proc2 = (0, _child_process.spawn)(make, [], { 254 | cwd: interpreterPath 255 | }); 256 | _context4.next = 20; 257 | return (0, _utils.waitUntilDone)(_proc2); 258 | 259 | case 20: 260 | _context4.next = 22; 261 | return _fsExtra2.default.copyAsync(_path2.default.join(interpreterPath, "wasm"), _path2.default.join(_init.buildDirectory.spec, "wasm")); 262 | 263 | case 22: 264 | console.log("Wasm Spec output: " + _init.binDirectory.spec); 265 | 266 | case 23: 267 | case "end": 268 | return _context4.stop(); 269 | } 270 | } 271 | }, _callee4, this); 272 | })); 273 | 274 | return function buildSpecInterpreter() { 275 | return _ref7.apply(this, arguments); 276 | }; 277 | }(); 278 | 279 | exports.default = build; 280 | 281 | var _init = require("./init"); 282 | 283 | var _path = require("path"); 284 | 285 | var _path2 = _interopRequireDefault(_path); 286 | 287 | var _fsExtra = require("fs-extra"); 288 | 289 | var _fsExtra2 = _interopRequireDefault(_fsExtra); 290 | 291 | var _klaw = require("klaw"); 292 | 293 | var _klaw2 = _interopRequireDefault(_klaw); 294 | 295 | var _dependencies = require("./dependencies"); 296 | 297 | var _child_process = require("child_process"); 298 | 299 | var _utils = require("./utils"); 300 | 301 | var _generate = require("./generate"); 302 | 303 | var _generate2 = _interopRequireDefault(_generate); 304 | 305 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 306 | 307 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 308 | 309 | function build() { 310 | return buildSpecInterpreter().catch(function (err) { 311 | return console.error("Error while building WebAssembly interpreter, validation will not be available\n" + err); 312 | }).then(buildCSmith).then(buildLLVM).then(prepareEmscriptenConfig) 313 | // Do one generation to trigger binaryen's build 314 | .then(function () { 315 | return (0, _generate2.default)() 316 | // It is possible to fail to generate a test file here, that doesn't mean the build failed... 317 | .catch(function (err) { 318 | return console.error(err); 319 | }); 320 | }); 321 | } -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 4 | 5 | var doGenerate = function () { 6 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(genFn, maxAttempts, args) { 7 | var nAttempts, lastError, _ref2, wasm, src, js, valid; 8 | 9 | return regeneratorRuntime.wrap(function _callee$(_context) { 10 | while (1) { 11 | switch (_context.prev = _context.next) { 12 | case 0: 13 | nAttempts = 0; 14 | lastError = null; 15 | 16 | case 2: 17 | if (!(maxAttempts === 0 || nAttempts < maxAttempts)) { 18 | _context.next = 25; 19 | break; 20 | } 21 | 22 | nAttempts++; 23 | _context.prev = 4; 24 | _context.next = 7; 25 | return genFn(args); 26 | 27 | case 7: 28 | _ref2 = _context.sent; 29 | wasm = _ref2.wasm; 30 | src = _ref2.src; 31 | js = _ref2.js; 32 | valid = _ref2.valid; 33 | 34 | console.log("Source file: " + src); 35 | console.log("Javascript file: " + js); 36 | console.log("WebAssembly file: " + wasm + (args.validate ? " is " + (valid ? "" : "not ") + "valid" : "")); 37 | return _context.abrupt("return"); 38 | 39 | case 18: 40 | _context.prev = 18; 41 | _context.t0 = _context["catch"](4); 42 | 43 | console.log("Failed to generate test"); 44 | console.error(_context.t0); 45 | lastError = _context.t0; 46 | 47 | case 23: 48 | _context.next = 2; 49 | break; 50 | 51 | case 25: 52 | throw lastError; 53 | 54 | case 26: 55 | case "end": 56 | return _context.stop(); 57 | } 58 | } 59 | }, _callee, this, [[4, 18]]); 60 | })); 61 | 62 | return function doGenerate(_x, _x2, _x3) { 63 | return _ref.apply(this, arguments); 64 | }; 65 | }(); 66 | 67 | var _yargs2 = require("yargs"); 68 | 69 | var _yargs3 = _interopRequireDefault(_yargs2); 70 | 71 | var _generate = require("./generate"); 72 | 73 | var _generate2 = _interopRequireDefault(_generate); 74 | 75 | var _init = require("./init"); 76 | 77 | var _build = require("./build"); 78 | 79 | var _build2 = _interopRequireDefault(_build); 80 | 81 | var _utils = require("./utils"); 82 | 83 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 84 | 85 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 86 | 87 | var commonGenOptions = { 88 | outdir: { 89 | alias: "o", 90 | describe: "Output directory for the generated files", 91 | default: _init.outputDir 92 | }, 93 | validate: { 94 | boolean: true, 95 | describe: "Run wasm-spec interpreter to validate generated file", 96 | default: true 97 | }, 98 | interpreted: { 99 | boolean: true, 100 | describe: "Generate interpreted version for comparison purposes", 101 | default: true 102 | }, 103 | inline: { 104 | boolean: true, 105 | describe: "Inline the WebAssembly module in the javascript file" 106 | }, 107 | silent: { 108 | alias: "s", 109 | boolean: true, 110 | describe: "Silently run generating tools" 111 | }, 112 | "timeout-em": { 113 | number: true, 114 | describe: "Timeout for emscripten", 115 | default: _utils.defaultTimeoutTranspiler 116 | }, 117 | "timeout-spec": { 118 | number: true, 119 | describe: "Timeout for spec interpreter", 120 | default: _utils.defaultTimeoutSpec 121 | } 122 | }; 123 | 124 | _yargs3.default.usage().help().alias("h", "help").wrap(Math.min(_yargs3.default.terminalWidth() - 1, 120)).command({ 125 | command: "config", 126 | desc: "Updates Emscripten configuration file for this machine", 127 | handler: function handler() { 128 | (0, _build.prepareEmscriptenConfig)().then(function () { 129 | return console.log("Configuration completed"); 130 | }).catch(function (err) { 131 | console.error(err); 132 | process.exit(1); 133 | }); 134 | } 135 | }).command({ 136 | command: "build", 137 | desc: "Build the tools needed", 138 | handler: function handler() { 139 | (0, _build2.default)().then(function () { 140 | return console.log("Build completed"); 141 | }).catch(function (err) { 142 | console.error(err); 143 | process.exit(1); 144 | }); 145 | } 146 | }).command({ 147 | command: "gen", 148 | desc: "generate a random WebAssembly test file", 149 | builder: function builder(_yargs) { 150 | return _yargs.options(_extends({}, commonGenOptions, { 151 | "timeout-csmith": { 152 | number: true, 153 | describe: "Timeout for csmith", 154 | default: _utils.defaultTimeoutCsmith 155 | }, 156 | attempts: { 157 | alias: "a", 158 | number: true, 159 | describe: "Number of attempts to generate a file, 0 for infinite", 160 | default: 1 161 | }, 162 | type: { 163 | alias: "t", 164 | string: true, 165 | describe: "Type of source file generated", 166 | choices: ["c", "cpp", "cpp11"], 167 | default: "c" 168 | }, 169 | fileName: { 170 | string: true, 171 | describe: "Name of the generated files (without extension)", 172 | default: "test" 173 | } 174 | })); 175 | }, 176 | handler: function handler(argv) { 177 | var args = { 178 | sourceType: _generate.sourceTypes[argv.type], 179 | validate: argv.validate, 180 | fileName: argv.fileName, 181 | outdir: argv.outdir, 182 | inlineWasm: argv.inline, 183 | timeoutSpec: argv.timeoutSpec, 184 | timeoutCsmith: argv.timeoutCsmith, 185 | timeoutTranspiler: argv.timeoutEm, 186 | execOptions: {} 187 | }; 188 | if (argv.silent) { 189 | args.execOptions.stdio = "ignore"; 190 | } 191 | doGenerate(_generate2.default, argv.attempts, args).catch(function () { 192 | return process.exit(1); 193 | }); 194 | } 195 | }).command({ 196 | command: "regen", 197 | desc: "generate WebAssembly from a .c source file", 198 | builder: function builder(_yargs) { 199 | return _yargs.options(_extends({}, commonGenOptions, { 200 | source: { 201 | required: true, 202 | string: true, 203 | describe: "Source file to regenerate" 204 | }, 205 | emargs: { 206 | string: true, 207 | describe: "Space separated extra arguments to pass to emcc" 208 | } 209 | })); 210 | }, 211 | handler: function handler(argv) { 212 | var args = { 213 | validate: argv.validate, 214 | sourceFile: argv.source, 215 | outdir: argv.outdir, 216 | inlineWasm: argv.inline, 217 | timeoutSpec: argv.timeoutSpec, 218 | timeoutTranspiler: argv.timeoutEm, 219 | execOptions: {}, 220 | emOptions: (argv.emargs || "").split(" ") 221 | }; 222 | if (argv.silent) { 223 | args.execOptions.stdio = "ignore"; 224 | } 225 | doGenerate(_generate.compileFromSource, 1, args).catch(function () { 226 | return process.exit(1); 227 | }); 228 | } 229 | }).demand(1).argv; -------------------------------------------------------------------------------- /lib/dependencies.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.generateDependencies = exports.specInterpreterDependencies = exports.emscriptenDependencies = exports.llvmDependencies = exports.csmithDependencies = undefined; 7 | 8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 9 | 10 | var fromPath = function () { 11 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(bin) { 12 | var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 13 | var rPath; 14 | return regeneratorRuntime.wrap(function _callee$(_context) { 15 | while (1) { 16 | switch (_context.prev = _context.next) { 17 | case 0: 18 | _context.prev = 0; 19 | _context.next = 3; 20 | return _which2.default.async(bin, opt); 21 | 22 | case 3: 23 | rPath = _context.sent; 24 | return _context.abrupt("return", rPath); 25 | 26 | case 7: 27 | _context.prev = 7; 28 | _context.t0 = _context["catch"](0); 29 | 30 | if (!(_context.t0.message.indexOf("not found") === -1)) { 31 | _context.next = 11; 32 | break; 33 | } 34 | 35 | throw _context.t0; 36 | 37 | case 11: 38 | return _context.abrupt("return", null); 39 | 40 | case 12: 41 | case "end": 42 | return _context.stop(); 43 | } 44 | } 45 | }, _callee, this, [[0, 7]]); 46 | })); 47 | 48 | return function fromPath(_x) { 49 | return _ref.apply(this, arguments); 50 | }; 51 | }(); 52 | 53 | var searchForMsBuild = function () { 54 | var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { 55 | var cache, getPaths, rPath; 56 | return regeneratorRuntime.wrap(function _callee2$(_context2) { 57 | while (1) { 58 | switch (_context2.prev = _context2.next) { 59 | case 0: 60 | if (_init.isWindows) { 61 | _context2.next = 2; 62 | break; 63 | } 64 | 65 | return _context2.abrupt("return", null); 66 | 67 | case 2: 68 | if (!msbuildCache) { 69 | _context2.next = 4; 70 | break; 71 | } 72 | 73 | return _context2.abrupt("return", msbuildCache); 74 | 75 | case 4: 76 | cache = function cache(p) { 77 | msbuildCache = p; 78 | console.log("MsBuild path: " + p); 79 | return msbuildCache; 80 | }; 81 | 82 | getPaths = function getPaths() { 83 | var dev15Paths = ["15.0"].map(function (version) { 84 | var binFolders = ["bin", "bin/x86", "bin/amd64"]; 85 | var vsVersions = ["2017", "Preview"]; 86 | var roots = [_path2.default.resolve(process.env.ProgramFiles, "Microsoft Visual Studio"), _path2.default.resolve(process.env["ProgramFiles(x86)"], "Microsoft Visual Studio")]; 87 | var allPaths = []; 88 | var _iteratorNormalCompletion = true; 89 | var _didIteratorError = false; 90 | var _iteratorError = undefined; 91 | 92 | try { 93 | for (var _iterator = roots[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 94 | var root = _step.value; 95 | var _iteratorNormalCompletion2 = true; 96 | var _didIteratorError2 = false; 97 | var _iteratorError2 = undefined; 98 | 99 | try { 100 | for (var _iterator2 = vsVersions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { 101 | var vsVersion = _step2.value; 102 | 103 | var folder = _path2.default.join(root, vsVersion); 104 | try { 105 | var content = _fsExtra2.default.readdirSync(folder); 106 | var _iteratorNormalCompletion3 = true; 107 | var _didIteratorError3 = false; 108 | var _iteratorError3 = undefined; 109 | 110 | try { 111 | for (var _iterator3 = content[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { 112 | var dir = _step3.value; 113 | var _iteratorNormalCompletion4 = true; 114 | var _didIteratorError4 = false; 115 | var _iteratorError4 = undefined; 116 | 117 | try { 118 | for (var _iterator4 = binFolders[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { 119 | var binFolder = _step4.value; 120 | 121 | allPaths.push(_path2.default.join(folder, dir, "msbuild", version, binFolder)); 122 | } 123 | } catch (err) { 124 | _didIteratorError4 = true; 125 | _iteratorError4 = err; 126 | } finally { 127 | try { 128 | if (!_iteratorNormalCompletion4 && _iterator4.return) { 129 | _iterator4.return(); 130 | } 131 | } finally { 132 | if (_didIteratorError4) { 133 | throw _iteratorError4; 134 | } 135 | } 136 | } 137 | } 138 | } catch (err) { 139 | _didIteratorError3 = true; 140 | _iteratorError3 = err; 141 | } finally { 142 | try { 143 | if (!_iteratorNormalCompletion3 && _iterator3.return) { 144 | _iterator3.return(); 145 | } 146 | } finally { 147 | if (_didIteratorError3) { 148 | throw _iteratorError3; 149 | } 150 | } 151 | } 152 | } catch (e) { 153 | // ignore 154 | } 155 | } 156 | } catch (err) { 157 | _didIteratorError2 = true; 158 | _iteratorError2 = err; 159 | } finally { 160 | try { 161 | if (!_iteratorNormalCompletion2 && _iterator2.return) { 162 | _iterator2.return(); 163 | } 164 | } finally { 165 | if (_didIteratorError2) { 166 | throw _iteratorError2; 167 | } 168 | } 169 | } 170 | } 171 | } catch (err) { 172 | _didIteratorError = true; 173 | _iteratorError = err; 174 | } finally { 175 | try { 176 | if (!_iteratorNormalCompletion && _iterator.return) { 177 | _iterator.return(); 178 | } 179 | } finally { 180 | if (_didIteratorError) { 181 | throw _iteratorError; 182 | } 183 | } 184 | } 185 | 186 | return allPaths.join(";"); 187 | }).join(";"); 188 | 189 | var oldDevPaths = ["14.0", "12.0", "10.0"].map(function (version) { 190 | return [_path2.default.resolve(process.env.ProgramFiles, "msbuild", version, "bin/x86"), _path2.default.resolve(process.env["ProgramFiles(x86)"], "msbuild", version, "bin"), _path2.default.resolve(process.env["ProgramFiles(x86)"], "msbuild", version, "bin/amd64")].join(";"); 191 | }).join(";"); 192 | return dev15Paths + oldDevPaths; 193 | }; 194 | 195 | _context2.next = 8; 196 | return fromPath("msbuild.exe"); 197 | 198 | case 8: 199 | _context2.t0 = _context2.sent; 200 | 201 | if (_context2.t0) { 202 | _context2.next = 13; 203 | break; 204 | } 205 | 206 | _context2.next = 12; 207 | return fromPath("msbuild.exe", { path: getPaths() }); 208 | 209 | case 12: 210 | _context2.t0 = _context2.sent; 211 | 212 | case 13: 213 | rPath = _context2.t0; 214 | 215 | if (!rPath) { 216 | _context2.next = 16; 217 | break; 218 | } 219 | 220 | return _context2.abrupt("return", cache(rPath)); 221 | 222 | case 16: 223 | throw new Error("MsBuild is missing"); 224 | 225 | case 17: 226 | case "end": 227 | return _context2.stop(); 228 | } 229 | } 230 | }, _callee2, this); 231 | })); 232 | 233 | return function searchForMsBuild() { 234 | return _ref2.apply(this, arguments); 235 | }; 236 | }(); 237 | 238 | var csmithDependencies = exports.csmithDependencies = function () { 239 | var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { 240 | var dependencies; 241 | return regeneratorRuntime.wrap(function _callee3$(_context3) { 242 | while (1) { 243 | switch (_context3.prev = _context3.next) { 244 | case 0: 245 | _context3.next = 2; 246 | return searchForMsBuild(); 247 | 248 | case 2: 249 | _context3.t0 = _context3.sent; 250 | _context3.next = 5; 251 | return _which2.default.async("cmake"); 252 | 253 | case 5: 254 | _context3.t1 = _context3.sent; 255 | dependencies = { 256 | msbuild: _context3.t0, 257 | cmake: _context3.t1 258 | }; 259 | 260 | if (!_init.isWindows) { 261 | _context3.next = 11; 262 | break; 263 | } 264 | 265 | dependencies.m4 = _path2.default.join(_init.thirdParties.m4, "m4.exe"); 266 | _context3.next = 14; 267 | break; 268 | 269 | case 11: 270 | _context3.next = 13; 271 | return _which2.default.async("m4"); 272 | 273 | case 13: 274 | dependencies.m4 = _context3.sent; 275 | 276 | case 14: 277 | return _context3.abrupt("return", dependencies); 278 | 279 | case 15: 280 | case "end": 281 | return _context3.stop(); 282 | } 283 | } 284 | }, _callee3, this); 285 | })); 286 | 287 | return function csmithDependencies() { 288 | return _ref3.apply(this, arguments); 289 | }; 290 | }(); 291 | 292 | var llvmDependencies = exports.llvmDependencies = function () { 293 | var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() { 294 | var dependencies; 295 | return regeneratorRuntime.wrap(function _callee4$(_context4) { 296 | while (1) { 297 | switch (_context4.prev = _context4.next) { 298 | case 0: 299 | _context4.next = 2; 300 | return _which2.default.async("cmake"); 301 | 302 | case 2: 303 | _context4.t0 = _context4.sent; 304 | _context4.next = 5; 305 | return searchForMsBuild(); 306 | 307 | case 5: 308 | _context4.t1 = _context4.sent; 309 | 310 | if (!_init.isWindows) { 311 | _context4.next = 10; 312 | break; 313 | } 314 | 315 | _context4.t2 = null; 316 | _context4.next = 13; 317 | break; 318 | 319 | case 10: 320 | _context4.next = 12; 321 | return _which2.default.async("make"); 322 | 323 | case 12: 324 | _context4.t2 = _context4.sent; 325 | 326 | case 13: 327 | _context4.t3 = _context4.t2; 328 | dependencies = { 329 | cmake: _context4.t0, 330 | msbuild: _context4.t1, 331 | make: _context4.t3 332 | }; 333 | return _context4.abrupt("return", dependencies); 334 | 335 | case 16: 336 | case "end": 337 | return _context4.stop(); 338 | } 339 | } 340 | }, _callee4, this); 341 | })); 342 | 343 | return function llvmDependencies() { 344 | return _ref4.apply(this, arguments); 345 | }; 346 | }(); 347 | 348 | var validatePythonVersion = function () { 349 | var _ref5 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5(python) { 350 | var info, version; 351 | return regeneratorRuntime.wrap(function _callee5$(_context5) { 352 | while (1) { 353 | switch (_context5.prev = _context5.next) { 354 | case 0: 355 | if (!python) { 356 | _context5.next = 18; 357 | break; 358 | } 359 | 360 | _context5.prev = 1; 361 | _context5.next = 4; 362 | return (0, _execa2.default)(python, ["--version"], { 363 | timeout: 30000 364 | }); 365 | 366 | case 4: 367 | info = _context5.sent; 368 | _context5.prev = 5; 369 | version = parseFloat(/python (\d+\.\d+)/ig.exec(info.stdout + info.stderr)[1]); 370 | 371 | if (!(version >= 2.7 && version < 3)) { 372 | _context5.next = 9; 373 | break; 374 | } 375 | 376 | return _context5.abrupt("return", python); 377 | 378 | case 9: 379 | _context5.next = 13; 380 | break; 381 | 382 | case 11: 383 | _context5.prev = 11; 384 | _context5.t0 = _context5["catch"](5); 385 | 386 | case 13: 387 | _context5.next = 18; 388 | break; 389 | 390 | case 15: 391 | _context5.prev = 15; 392 | _context5.t1 = _context5["catch"](1); 393 | 394 | console.error(_context5.t1); 395 | 396 | case 18: 397 | return _context5.abrupt("return", null); 398 | 399 | case 19: 400 | case "end": 401 | return _context5.stop(); 402 | } 403 | } 404 | }, _callee5, this, [[1, 15], [5, 11]]); 405 | })); 406 | 407 | return function validatePythonVersion(_x3) { 408 | return _ref5.apply(this, arguments); 409 | }; 410 | }(); 411 | 412 | var searchPython = function () { 413 | var _ref6 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() { 414 | var locations, _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, loc, _arr, _i, bin, python; 415 | 416 | return regeneratorRuntime.wrap(function _callee6$(_context6) { 417 | while (1) { 418 | switch (_context6.prev = _context6.next) { 419 | case 0: 420 | if (!pythonCache) { 421 | _context6.next = 2; 422 | break; 423 | } 424 | 425 | return _context6.abrupt("return", pythonCache); 426 | 427 | case 2: 428 | locations = [undefined, // Use current path 429 | process.env.PYTHON]; 430 | 431 | if (_init.isWindows) { 432 | locations.push("C:\\Python27"); 433 | } 434 | _iteratorNormalCompletion5 = true; 435 | _didIteratorError5 = false; 436 | _iteratorError5 = undefined; 437 | _context6.prev = 7; 438 | _iterator5 = locations[Symbol.iterator](); 439 | 440 | case 9: 441 | if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) { 442 | _context6.next = 32; 443 | break; 444 | } 445 | 446 | loc = _step5.value; 447 | _arr = ["python", "python2"]; 448 | _i = 0; 449 | 450 | case 13: 451 | if (!(_i < _arr.length)) { 452 | _context6.next = 29; 453 | break; 454 | } 455 | 456 | bin = _arr[_i]; 457 | _context6.t0 = validatePythonVersion; 458 | _context6.next = 18; 459 | return fromPath(bin, { path: loc }); 460 | 461 | case 18: 462 | _context6.t1 = _context6.sent; 463 | _context6.next = 21; 464 | return (0, _context6.t0)(_context6.t1); 465 | 466 | case 21: 467 | python = _context6.sent; 468 | 469 | if (!python) { 470 | _context6.next = 26; 471 | break; 472 | } 473 | 474 | pythonCache = python; 475 | console.log("Python path: " + python); 476 | return _context6.abrupt("return", python); 477 | 478 | case 26: 479 | _i++; 480 | _context6.next = 13; 481 | break; 482 | 483 | case 29: 484 | _iteratorNormalCompletion5 = true; 485 | _context6.next = 9; 486 | break; 487 | 488 | case 32: 489 | _context6.next = 38; 490 | break; 491 | 492 | case 34: 493 | _context6.prev = 34; 494 | _context6.t2 = _context6["catch"](7); 495 | _didIteratorError5 = true; 496 | _iteratorError5 = _context6.t2; 497 | 498 | case 38: 499 | _context6.prev = 38; 500 | _context6.prev = 39; 501 | 502 | if (!_iteratorNormalCompletion5 && _iterator5.return) { 503 | _iterator5.return(); 504 | } 505 | 506 | case 41: 507 | _context6.prev = 41; 508 | 509 | if (!_didIteratorError5) { 510 | _context6.next = 44; 511 | break; 512 | } 513 | 514 | throw _iteratorError5; 515 | 516 | case 44: 517 | return _context6.finish(41); 518 | 519 | case 45: 520 | return _context6.finish(38); 521 | 522 | case 46: 523 | throw new Error("Python 2 is missing"); 524 | 525 | case 47: 526 | case "end": 527 | return _context6.stop(); 528 | } 529 | } 530 | }, _callee6, this, [[7, 34, 38, 46], [39,, 41, 45]]); 531 | })); 532 | 533 | return function searchPython() { 534 | return _ref6.apply(this, arguments); 535 | }; 536 | }(); 537 | 538 | var emscriptenDependencies = exports.emscriptenDependencies = function () { 539 | var _ref7 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() { 540 | return regeneratorRuntime.wrap(function _callee7$(_context7) { 541 | while (1) { 542 | switch (_context7.prev = _context7.next) { 543 | case 0: 544 | _context7.t0 = emCache; 545 | 546 | if (_context7.t0) { 547 | _context7.next = 5; 548 | break; 549 | } 550 | 551 | _context7.next = 4; 552 | return emscriptenDependenciesInternal(); 553 | 554 | case 4: 555 | _context7.t0 = _context7.sent; 556 | 557 | case 5: 558 | emCache = _context7.t0; 559 | return _context7.abrupt("return", emCache); 560 | 561 | case 7: 562 | case "end": 563 | return _context7.stop(); 564 | } 565 | } 566 | }, _callee7, this); 567 | })); 568 | 569 | return function emscriptenDependencies() { 570 | return _ref7.apply(this, arguments); 571 | }; 572 | }(); 573 | 574 | var emscriptenDependenciesInternal = function () { 575 | var _ref8 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() { 576 | var python, emscriptenRoot, emcc, empp, runFn; 577 | return regeneratorRuntime.wrap(function _callee8$(_context8) { 578 | while (1) { 579 | switch (_context8.prev = _context8.next) { 580 | case 0: 581 | _context8.next = 2; 582 | return searchPython(); 583 | 584 | case 2: 585 | python = _context8.sent; 586 | emscriptenRoot = _init.thirdParties.emscripten; 587 | emcc = _path2.default.join(emscriptenRoot, "emcc.py"); 588 | empp = _path2.default.join(emscriptenRoot, "em++.py"); 589 | 590 | runFn = function runFn(bin) { 591 | return function () { 592 | var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 593 | var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 594 | 595 | var proc = (0, _execa2.default)(python, [bin].concat(_toConsumableArray(args)), _extends({ 596 | stdio: "inherit", 597 | env: _extends({}, process.env, { 598 | // Change home to tell emscripten to use our .emscripten file 599 | HOME: _init.outputDir, 600 | USERPROFILE: _init.outputDir 601 | }) 602 | }, opt)); 603 | return proc; 604 | }; 605 | }; 606 | 607 | return _context8.abrupt("return", { 608 | python: python, 609 | emcc: emcc, 610 | empp: empp, 611 | runEmcc: runFn(emcc), 612 | runEmpp: runFn(empp) 613 | }); 614 | 615 | case 8: 616 | case "end": 617 | return _context8.stop(); 618 | } 619 | } 620 | }, _callee8, this); 621 | })); 622 | 623 | return function emscriptenDependenciesInternal() { 624 | return _ref8.apply(this, arguments); 625 | }; 626 | }(); 627 | 628 | var specInterpreterDependencies = exports.specInterpreterDependencies = function () { 629 | var _ref9 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() { 630 | return regeneratorRuntime.wrap(function _callee9$(_context9) { 631 | while (1) { 632 | switch (_context9.prev = _context9.next) { 633 | case 0: 634 | _context9.t0 = specCache; 635 | 636 | if (_context9.t0) { 637 | _context9.next = 5; 638 | break; 639 | } 640 | 641 | _context9.next = 4; 642 | return specInterpreterDependenciesInternal(); 643 | 644 | case 4: 645 | _context9.t0 = _context9.sent; 646 | 647 | case 5: 648 | specCache = _context9.t0; 649 | return _context9.abrupt("return", specCache); 650 | 651 | case 7: 652 | case "end": 653 | return _context9.stop(); 654 | } 655 | } 656 | }, _callee9, this); 657 | })); 658 | 659 | return function specInterpreterDependencies() { 660 | return _ref9.apply(this, arguments); 661 | }; 662 | }(); 663 | 664 | var specInterpreterDependenciesInternal = function () { 665 | var _ref10 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() { 666 | return regeneratorRuntime.wrap(function _callee10$(_context10) { 667 | while (1) { 668 | switch (_context10.prev = _context10.next) { 669 | case 0: 670 | if (!_init.isWindows) { 671 | _context10.next = 2; 672 | break; 673 | } 674 | 675 | return _context10.abrupt("return", { 676 | cmd: "cmd" 677 | }); 678 | 679 | case 2: 680 | _context10.next = 4; 681 | return _which2.default.async("make"); 682 | 683 | case 4: 684 | _context10.t0 = _context10.sent; 685 | return _context10.abrupt("return", { 686 | make: _context10.t0 687 | }); 688 | 689 | case 6: 690 | case "end": 691 | return _context10.stop(); 692 | } 693 | } 694 | }, _callee10, this); 695 | })); 696 | 697 | return function specInterpreterDependenciesInternal() { 698 | return _ref10.apply(this, arguments); 699 | }; 700 | }(); 701 | 702 | var generateDependencies = exports.generateDependencies = function () { 703 | var _ref11 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() { 704 | return regeneratorRuntime.wrap(function _callee11$(_context11) { 705 | while (1) { 706 | switch (_context11.prev = _context11.next) { 707 | case 0: 708 | _context11.t0 = genDepCache; 709 | 710 | if (_context11.t0) { 711 | _context11.next = 5; 712 | break; 713 | } 714 | 715 | _context11.next = 4; 716 | return generateDependenciesInternal(); 717 | 718 | case 4: 719 | _context11.t0 = _context11.sent; 720 | 721 | case 5: 722 | genDepCache = _context11.t0; 723 | return _context11.abrupt("return", genDepCache); 724 | 725 | case 7: 726 | case "end": 727 | return _context11.stop(); 728 | } 729 | } 730 | }, _callee11, this); 731 | })); 732 | 733 | return function generateDependencies() { 734 | return _ref11.apply(this, arguments); 735 | }; 736 | }(); 737 | 738 | var generateDependenciesInternal = function () { 739 | var _ref12 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee12() { 740 | var wasmExe, csmithExe, clangExe; 741 | return regeneratorRuntime.wrap(function _callee12$(_context12) { 742 | while (1) { 743 | switch (_context12.prev = _context12.next) { 744 | case 0: 745 | _context12.next = 2; 746 | return fromPath("wasm"); 747 | 748 | case 2: 749 | _context12.t0 = _context12.sent; 750 | 751 | if (_context12.t0) { 752 | _context12.next = 7; 753 | break; 754 | } 755 | 756 | _context12.next = 6; 757 | return _which2.default.async("wasm", { path: _init.binDirectory.spec }); 758 | 759 | case 6: 760 | _context12.t0 = _context12.sent; 761 | 762 | case 7: 763 | wasmExe = _context12.t0; 764 | _context12.next = 10; 765 | return fromPath("csmith"); 766 | 767 | case 10: 768 | _context12.t1 = _context12.sent; 769 | 770 | if (_context12.t1) { 771 | _context12.next = 15; 772 | break; 773 | } 774 | 775 | _context12.next = 14; 776 | return _which2.default.async("csmith", { path: _init.binDirectory.csmith }); 777 | 778 | case 14: 779 | _context12.t1 = _context12.sent; 780 | 781 | case 15: 782 | csmithExe = _context12.t1; 783 | _context12.next = 18; 784 | return fromPath("clang"); 785 | 786 | case 18: 787 | _context12.t2 = _context12.sent; 788 | 789 | if (_context12.t2) { 790 | _context12.next = 23; 791 | break; 792 | } 793 | 794 | _context12.next = 22; 795 | return _which2.default.async("clang", { path: _init.binDirectory.llvm }); 796 | 797 | case 22: 798 | _context12.t2 = _context12.sent; 799 | 800 | case 23: 801 | clangExe = _context12.t2; 802 | _context12.t3 = _extends; 803 | _context12.t4 = { 804 | wasm: wasmExe, 805 | csmith: csmithExe, 806 | clang: clangExe 807 | }; 808 | _context12.next = 28; 809 | return emscriptenDependencies(); 810 | 811 | case 28: 812 | _context12.t5 = _context12.sent; 813 | return _context12.abrupt("return", (0, _context12.t3)(_context12.t4, _context12.t5)); 814 | 815 | case 30: 816 | case "end": 817 | return _context12.stop(); 818 | } 819 | } 820 | }, _callee12, this); 821 | })); 822 | 823 | return function generateDependenciesInternal() { 824 | return _ref12.apply(this, arguments); 825 | }; 826 | }(); 827 | 828 | var _init = require("./init"); 829 | 830 | var _path = require("path"); 831 | 832 | var _path2 = _interopRequireDefault(_path); 833 | 834 | var _fsExtra = require("fs-extra"); 835 | 836 | var _fsExtra2 = _interopRequireDefault(_fsExtra); 837 | 838 | var _execa = require("execa"); 839 | 840 | var _execa2 = _interopRequireDefault(_execa); 841 | 842 | var _which = require("which"); 843 | 844 | var _which2 = _interopRequireDefault(_which); 845 | 846 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 847 | 848 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 849 | 850 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 851 | 852 | var msbuildCache = void 0; 853 | 854 | var pythonCache = void 0; 855 | 856 | var emCache = void 0; 857 | 858 | var specCache = void 0; 859 | 860 | 861 | var genDepCache = void 0; -------------------------------------------------------------------------------- /lib/generate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.compileFromSource = exports.sourceTypes = undefined; 7 | 8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 9 | 10 | var compileFromSource = exports.compileFromSource = function () { 11 | var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(config) { 12 | var _ref5, wasm, runEmcc; 13 | 14 | return regeneratorRuntime.wrap(function _callee2$(_context2) { 15 | while (1) { 16 | switch (_context2.prev = _context2.next) { 17 | case 0: 18 | _context2.next = 2; 19 | return (0, _dependencies.generateDependencies)(); 20 | 21 | case 2: 22 | _ref5 = _context2.sent; 23 | wasm = _ref5.wasm; 24 | runEmcc = _ref5.runEmcc; 25 | return _context2.abrupt("return", compile(_extends({}, config, { 26 | transpiler: runEmcc, 27 | specInterpreter: wasm 28 | }))); 29 | 30 | case 6: 31 | case "end": 32 | return _context2.stop(); 33 | } 34 | } 35 | }, _callee2, this); 36 | })); 37 | 38 | return function compileFromSource(_x2) { 39 | return _ref4.apply(this, arguments); 40 | }; 41 | }(); 42 | 43 | var compile = function () { 44 | var _ref6 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { 45 | var _ref7 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 46 | sourceFile = _ref7.sourceFile, 47 | _ref7$validate = _ref7.validate, 48 | validate = _ref7$validate === undefined ? true : _ref7$validate, 49 | _ref7$interpreted = _ref7.interpreted, 50 | interpreted = _ref7$interpreted === undefined ? true : _ref7$interpreted, 51 | _ref7$fileName = _ref7.fileName, 52 | fileName = _ref7$fileName === undefined ? "test" : _ref7$fileName, 53 | _ref7$outdir = _ref7.outdir, 54 | outdir = _ref7$outdir === undefined ? _init.outputDir : _ref7$outdir, 55 | _ref7$inlineWasm = _ref7.inlineWasm, 56 | inlineWasm = _ref7$inlineWasm === undefined ? false : _ref7$inlineWasm, 57 | _ref7$execOptions = _ref7.execOptions, 58 | execOptions = _ref7$execOptions === undefined ? {} : _ref7$execOptions, 59 | _ref7$timeoutSpec = _ref7.timeoutSpec, 60 | timeoutSpec = _ref7$timeoutSpec === undefined ? _utils.defaultTimeoutSpec : _ref7$timeoutSpec, 61 | _ref7$timeoutTranspil = _ref7.timeoutTranspiler, 62 | timeoutTranspiler = _ref7$timeoutTranspil === undefined ? _utils.defaultTimeoutTranspiler : _ref7$timeoutTranspil, 63 | _ref7$emOptions = _ref7.emOptions, 64 | emOptions = _ref7$emOptions === undefined ? [] : _ref7$emOptions, 65 | transpiler = _ref7.transpiler, 66 | specInterpreter = _ref7.specInterpreter; 67 | 68 | var shouldInlineWasm, jsFile, clangFlags, isCpp11, wasmFile, wastFile, isValid, wasmSpecOutput, match, specErrorMessage, newJsFile, oldFile, fd, wasmBuffer, string, i, buf, nodeRegenOption; 69 | return regeneratorRuntime.wrap(function _callee3$(_context3) { 70 | while (1) { 71 | switch (_context3.prev = _context3.next) { 72 | case 0: 73 | _context3.next = 2; 74 | return _fsExtra2.default.ensureDirAsync(outdir); 75 | 76 | case 2: 77 | shouldInlineWasm = inlineWasm; 78 | // Generate wasm version 79 | 80 | jsFile = _path2.default.resolve(outdir, fileName + ".js"); 81 | clangFlags = ["-w"]; 82 | isCpp11 = sourceFile.endsWith(".cpp"); 83 | 84 | if (isCpp11) { 85 | clangFlags.push("-std=c++11", "-Wno-c++11-narrowing"); 86 | } 87 | _context3.next = 9; 88 | return transpiler([sourceFile, "-I" + _path2.default.join(_init.thirdParties.csmith, "runtime"), "-I" + _path2.default.join(_init.buildDirectory.csmith, "runtime"), "-s", "WASM=1", "-s", "BINARYEN_METHOD='native-wasm" + (interpreted ? ",interpret-binary" : "") + "'", "-s", "BINARYEN_ASYNC_COMPILATION=0", "-g"].concat(clangFlags, _toConsumableArray(emOptions), ["-o", jsFile]), _extends({ cwd: outdir }, execOptions, { timeout: timeoutTranspiler })); 89 | 90 | case 9: 91 | wasmFile = _path2.default.resolve(outdir, fileName + ".wasm"); 92 | wastFile = _path2.default.resolve(outdir, fileName + ".wast"); 93 | 94 | // Make sure it is valid 95 | // Emscripten sometimes generates invalid wasm file, should investigate 96 | 97 | isValid = true; 98 | wasmSpecOutput = null; 99 | 100 | if (!(validate && specInterpreter)) { 101 | _context3.next = 36; 102 | break; 103 | } 104 | 105 | _context3.prev = 14; 106 | _context3.next = 17; 107 | return (0, _execa2.default)(specInterpreter, [wasmFile, "-d"], _extends({ 108 | cwd: outdir 109 | }, execOptions, { 110 | timeout: timeoutSpec 111 | })); 112 | 113 | case 17: 114 | wasmSpecOutput = _context3.sent; 115 | _context3.next = 34; 116 | break; 117 | 118 | case 20: 119 | _context3.prev = 20; 120 | _context3.t0 = _context3["catch"](14); 121 | 122 | if (!(_context3.t0.timedOut || _context3.t0.killed)) { 123 | _context3.next = 26; 124 | break; 125 | } 126 | 127 | // Ignore spec interpreter result if it timed out 128 | console.warn("Spec interpreter timed out"); 129 | _context3.next = 34; 130 | break; 131 | 132 | case 26: 133 | wasmSpecOutput = { stdout: _context3.t0.stdout, stderr: _context3.t0.stderr }; 134 | isValid = false; 135 | shouldInlineWasm = true; 136 | match = /0x[0-9a-f]+:(.+)$/mi.exec(wasmSpecOutput.stderr); 137 | specErrorMessage = match && match[1] || wasmSpecOutput.stdout + wasmSpecOutput.stderr; 138 | newJsFile = "\n// check environment for wasm regen\nvar ENVIRONMENT_IS_NODE = typeof require !== \"undefined\";\nif (ENVIRONMENT_IS_NODE) {\n Module[\"arguments\"] = process.argv.slice(1);\n}\n\n// {{PRE_RUN_ADDITIONS}}\nif (WebAssembly.validate(Module[\"readBinary\"]())) {\n throw new Error(`Fatal error: Expected binary to be invalid because: " + specErrorMessage + "`);\n}\n// {{POST_RUN_ADDITIONS}}\n"; 139 | _context3.next = 34; 140 | return _fsExtra2.default.writeFileAsync(jsFile, newJsFile); 141 | 142 | case 34: 143 | _context3.next = 37; 144 | break; 145 | 146 | case 36: 147 | if (validate) { 148 | console.warn("WebAssembly spec interpreter is missing, unable to do validation"); 149 | } 150 | 151 | case 37: 152 | _context3.next = 39; 153 | return _fsExtra2.default.readFileAsync(jsFile); 154 | 155 | case 39: 156 | oldFile = _context3.sent.toString(); 157 | _context3.next = 42; 158 | return _fsExtra2.default.openAsync(jsFile, "w"); 159 | 160 | case 42: 161 | fd = _context3.sent; 162 | _context3.next = 45; 163 | return _fsExtra2.default.writeAsync(fd, "// Generated with options: " + emOptions.join(" ") + "\n\n"); 164 | 165 | case 45: 166 | if (!shouldInlineWasm) { 167 | _context3.next = 57; 168 | break; 169 | } 170 | 171 | _context3.next = 48; 172 | return _fsExtra2.default.readFileAsync(wasmFile, "binary"); 173 | 174 | case 48: 175 | wasmBuffer = _context3.sent; 176 | string = ""; 177 | 178 | for (i = 0; i < wasmBuffer.length; ++i) { 179 | string += "\\x" + wasmBuffer.charCodeAt(i).toString(16).padStart(2, "0"); 180 | } 181 | buf = "\n// {{PRE_WASM_EXPRGEN}}\nvar Module = Module || {};\nvar wasmBuffer = \"" + string + "\";\nModule[\"readBinary\"] = function(file) {\n var buffer = new ArrayBuffer(wasmBuffer.length);\n var view = new Uint8Array(buffer);\n for (var i = 0; i < wasmBuffer.length; ++i) {\n view[i] = wasmBuffer.charCodeAt(i);\n }\n return view;\n}\n\n\n// {{POST_WASM_EXPRGEN}}\n\n\n"; 182 | _context3.next = 54; 183 | return _fsExtra2.default.writeAsync(fd, buf); 184 | 185 | case 54: 186 | nodeRegenOption = "\nif (ENVIRONMENT_IS_NODE && Module[\"arguments\"].indexOf(\"--regen-wasm\") !== -1) {\n var wasmBinaryFile = Module['wasmBinaryFile'] || \"" + _path2.default.basename(wasmFile) + "\";\n var bin = Module[\"readBinary\"](wasmBinaryFile);\n var wstream = require(\"fs\").createWriteStream(wasmBinaryFile, {defaultEncoding: \"binary\"});\n wstream.write(Buffer.from(bin.buffer));\n wstream.end();\n wstream.on(\"finish\", function() {\n console.log(\"WebAssembly binary buffer written to \" + wasmBinaryFile);\n });\n} else {\n"; 187 | 188 | oldFile = oldFile.replace("{{PRE_RUN_ADDITIONS}}", "{{PRE_RUN_ADDITIONS}}\n" + nodeRegenOption); 189 | oldFile = oldFile.replace("// {{POST_RUN_ADDITIONS}}", "}\n// {{POST_RUN_ADDITIONS}}"); 190 | 191 | case 57: 192 | _context3.next = 59; 193 | return _fsExtra2.default.writeAsync(fd, oldFile); 194 | 195 | case 59: 196 | _context3.next = 61; 197 | return _fsExtra2.default.closeAsync(fd); 198 | 199 | case 61: 200 | return _context3.abrupt("return", { 201 | src: sourceFile, 202 | js: jsFile, 203 | wasm: wasmFile, 204 | wast: wastFile, 205 | valid: isValid, 206 | wasmSpecOutput: wasmSpecOutput 207 | }); 208 | 209 | case 62: 210 | case "end": 211 | return _context3.stop(); 212 | } 213 | } 214 | }, _callee3, this, [[14, 20]]); 215 | })); 216 | 217 | return function compile() { 218 | return _ref6.apply(this, arguments); 219 | }; 220 | }(); 221 | 222 | var _init = require("./init"); 223 | 224 | var _fsExtra = require("fs-extra"); 225 | 226 | var _fsExtra2 = _interopRequireDefault(_fsExtra); 227 | 228 | var _path = require("path"); 229 | 230 | var _path2 = _interopRequireDefault(_path); 231 | 232 | var _dependencies = require("./dependencies"); 233 | 234 | var _execa = require("execa"); 235 | 236 | var _execa2 = _interopRequireDefault(_execa); 237 | 238 | var _utils = require("./utils"); 239 | 240 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 241 | 242 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 243 | 244 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } 245 | 246 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 247 | 248 | var sourceTypes = exports.sourceTypes = { 249 | c: Symbol(), 250 | cpp: Symbol(), 251 | cpp11: Symbol() 252 | }; 253 | 254 | exports.default = function () { 255 | var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { 256 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 257 | 258 | var _ref$sourceType = _ref.sourceType, 259 | sourceType = _ref$sourceType === undefined ? sourceTypes.c : _ref$sourceType, 260 | _ref$fileName = _ref.fileName, 261 | fileName = _ref$fileName === undefined ? "test" : _ref$fileName, 262 | _ref$outdir = _ref.outdir, 263 | outdir = _ref$outdir === undefined ? _init.outputDir : _ref$outdir, 264 | _ref$execOptions = _ref.execOptions, 265 | execOptions = _ref$execOptions === undefined ? {} : _ref$execOptions, 266 | _ref$timeoutCsmith = _ref.timeoutCsmith, 267 | timeoutCsmith = _ref$timeoutCsmith === undefined ? _utils.defaultTimeoutCsmith : _ref$timeoutCsmith, 268 | args = _objectWithoutProperties(_ref, ["sourceType", "fileName", "outdir", "execOptions", "timeoutCsmith"]); 269 | 270 | var _ref3, csmith, wasm, runEmcc, runEmpp, sourceFile, csmithArgs, transpiler, csmithProc, randomOptions; 271 | 272 | return regeneratorRuntime.wrap(function _callee$(_context) { 273 | while (1) { 274 | switch (_context.prev = _context.next) { 275 | case 0: 276 | _context.next = 2; 277 | return (0, _dependencies.generateDependencies)(); 278 | 279 | case 2: 280 | _ref3 = _context.sent; 281 | csmith = _ref3.csmith; 282 | wasm = _ref3.wasm; 283 | runEmcc = _ref3.runEmcc; 284 | runEmpp = _ref3.runEmpp; 285 | _context.next = 9; 286 | return _fsExtra2.default.ensureDirAsync(outdir); 287 | 288 | case 9: 289 | 290 | // Generate random c file 291 | sourceFile = _path2.default.resolve(outdir, fileName); 292 | csmithArgs = []; 293 | transpiler = void 0; 294 | _context.t0 = sourceType; 295 | _context.next = _context.t0 === sourceTypes.c ? 15 : _context.t0 === sourceTypes.cpp11 ? 18 : _context.t0 === sourceTypes.cpp ? 19 : 23; 296 | break; 297 | 298 | case 15: 299 | sourceFile += ".c"; 300 | transpiler = runEmcc; 301 | return _context.abrupt("break", 24); 302 | 303 | case 18: 304 | csmithArgs.push("--cpp11"); 305 | 306 | case 19: 307 | sourceFile += ".cpp"; 308 | csmithArgs.push("--lang-cpp"); 309 | transpiler = runEmpp; 310 | return _context.abrupt("break", 24); 311 | 312 | case 23: 313 | throw new Error("Unknown source type"); 314 | 315 | case 24: 316 | csmithProc = (0, _execa2.default)(csmith, ["-o", sourceFile].concat(csmithArgs), _extends({ 317 | stdio: "inherit", 318 | cwd: outdir 319 | }, execOptions, { 320 | timeout: timeoutCsmith 321 | })); 322 | _context.next = 27; 323 | return csmithProc; 324 | 325 | case 27: 326 | randomOptions = getRandomEmscriptenOptions(); 327 | return _context.abrupt("return", compile(_extends({}, args, { 328 | sourceFile: sourceFile, 329 | fileName: fileName, 330 | outdir: outdir, 331 | execOptions: execOptions, 332 | emOptions: randomOptions, 333 | transpiler: transpiler, 334 | specInterpreter: wasm 335 | }))); 336 | 337 | case 29: 338 | case "end": 339 | return _context.stop(); 340 | } 341 | } 342 | }, _callee, this); 343 | })); 344 | 345 | function generate() { 346 | return _ref2.apply(this, arguments); 347 | } 348 | 349 | return generate; 350 | }(); 351 | 352 | function getRandomEmscriptenOptions() { 353 | // Returns a random integer between min (included) and max (excluded) 354 | // Using Math.round() will give you a non-uniform distribution! 355 | function getRandomInt(_max) { 356 | var _min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; 357 | 358 | var min = Math.ceil(_min); 359 | var max = Math.floor(_max) + 1; 360 | return Math.floor(Math.random() * (max - min)) + min; 361 | } 362 | // odds: chance to be true out of 10 363 | function roll(odds) { 364 | var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10; 365 | 366 | return getRandomInt(max) < odds; 367 | } 368 | var optimizationLevel = getRandomInt(5); 369 | var options = ["-O" + (optimizationLevel < 4 ? optimizationLevel : optimizationLevel < 5 ? "s" : "z"), "-s", "RETAIN_COMPILER_SETTINGS=1"]; 370 | 371 | var changedDefaultMem = false; 372 | var totalStack = 5 * 1024 * 1024; 373 | if (roll(3)) { 374 | changedDefaultMem = true; 375 | totalStack = getRandomInt(20, 1) * 1024 * 1024; 376 | options.push("-s", "TOTAL_STACK=" + totalStack); 377 | } 378 | 379 | var doMemGrowth = roll(3); 380 | var wasmPageSize = 65536; 381 | if (doMemGrowth) { 382 | options.push("-s", "ALLOW_MEMORY_GROWTH=1"); 383 | } 384 | 385 | var totalMem = 256 * wasmPageSize; 386 | if (!changedDefaultMem) { 387 | var changeTotalMem = roll(5); 388 | if (changeTotalMem) { 389 | changedDefaultMem = true; 390 | totalMem = wasmPageSize * getRandomInt(16000) | 0; 391 | } 392 | } 393 | 394 | if (changedDefaultMem) { 395 | var totalMemory = wasmPageSize; 396 | while (totalMemory < totalMem || totalMemory < 2 * totalStack) { 397 | if (totalMemory < 16 * 1024 * 1024) { 398 | totalMemory *= 2; 399 | } else { 400 | totalMemory += 16 * 1024 * 1024; 401 | } 402 | } 403 | options.push("-s", "TOTAL_MEMORY=" + (totalMemory >>> 0)); 404 | } 405 | 406 | /* Currently not supported for WebAssembly 407 | if (roll(2)) { 408 | let splitMem = getRandomInt(totalMem, totalStack); 409 | splitMem = 1 << Math.log2(splitMem); 410 | if (splitMem < totalStack) { 411 | splitMem *= 2; 412 | } 413 | if (splitMem <= totalMem) { 414 | options.push("-s", `SPLIT_MEMORY=${splitMem}`); 415 | } 416 | } 417 | */ 418 | 419 | if (roll(1, 20)) { 420 | options.push("-s", "GLOBAL_BASE=" + getRandomInt(1024)); 421 | } 422 | 423 | if (roll(1, 20)) { 424 | options.push("-s", "STACK_START=" + getRandomInt(1024)); 425 | } 426 | 427 | if (roll(1, 20)) { 428 | options.push("-s", "DOUBLE_MODE=0"); 429 | } 430 | 431 | if (roll(2)) { 432 | options.push("-s", "PRECISE_I64_MATH=" + getRandomInt(2)); 433 | } 434 | 435 | if (roll(2)) { 436 | options.push("-s", "AGGRESSIVE_VARIABLE_ELIMINATION=1"); 437 | } 438 | 439 | if (roll(2)) { 440 | options.push("-s", "EMULATED_FUNCTION_POINTERS=" + getRandomInt(2, 1)); 441 | } else if (roll(2)) { 442 | options.push("-s", "EMULATE_FUNCTION_POINTER_CASTS=1"); 443 | } 444 | 445 | return options; 446 | } -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.regen = exports.generate = undefined; 7 | 8 | var _generate = require("./generate"); 9 | 10 | var _generate2 = _interopRequireDefault(_generate); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 13 | 14 | exports.generate = _generate2.default; 15 | exports.regen = _generate.compileFromSource; -------------------------------------------------------------------------------- /lib/init.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.thirdParties = exports.thirdPartyRoot = exports.outputDir = exports.binDirectory = exports.buildDirectory = exports.toolsDirectory = exports.rootDir = exports.isWindows = undefined; 7 | 8 | var _bluebird = require("bluebird"); 9 | 10 | var _bluebird2 = _interopRequireDefault(_bluebird); 11 | 12 | var _fsExtra = require("fs-extra"); 13 | 14 | var _fsExtra2 = _interopRequireDefault(_fsExtra); 15 | 16 | var _which = require("which"); 17 | 18 | var _which2 = _interopRequireDefault(_which); 19 | 20 | var _child_process = require("child_process"); 21 | 22 | var _child_process2 = _interopRequireDefault(_child_process); 23 | 24 | var _path = require("path"); 25 | 26 | var _path2 = _interopRequireDefault(_path); 27 | 28 | var _os = require("os"); 29 | 30 | var _os2 = _interopRequireDefault(_os); 31 | 32 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 33 | 34 | var isWindows = exports.isWindows = _os2.default.platform() === "win32"; 35 | 36 | _bluebird2.default.promisifyAll(_fsExtra2.default); 37 | _which2.default.async = _bluebird2.default.promisify(_which2.default); 38 | _bluebird2.default.promisifyAll(_child_process2.default); 39 | 40 | var rootDir = exports.rootDir = _path2.default.join(__dirname, ".."); 41 | var toolsDirectory = exports.toolsDirectory = _path2.default.join(rootDir, "tools"); 42 | var buildDirectory = exports.buildDirectory = { 43 | csmith: _path2.default.join(toolsDirectory, "csmith"), 44 | spec: _path2.default.join(toolsDirectory, "spec"), 45 | llvm: _path2.default.join(toolsDirectory, "llvm") 46 | }; 47 | var binDirectory = exports.binDirectory = { 48 | csmith: _path2.default.join(buildDirectory.csmith, "src", "Release"), 49 | spec: buildDirectory.spec, 50 | llvm: _path2.default.join(buildDirectory.llvm, isWindows ? "Release/bin" : "bin") 51 | }; 52 | var outputDir = exports.outputDir = _path2.default.join(rootDir, "output"); 53 | var thirdPartyRoot = exports.thirdPartyRoot = _path2.default.join(rootDir, "third_party"); 54 | var thirdParties = exports.thirdParties = { 55 | llvm: _path2.default.join(thirdPartyRoot, "emscripten-fastcomp"), 56 | emscripten: _path2.default.join(thirdPartyRoot, "emscripten"), 57 | csmith: _path2.default.join(thirdPartyRoot, "csmith"), 58 | clang: _path2.default.join(thirdPartyRoot, "clang"), 59 | spec: _path2.default.join(thirdPartyRoot, "wasm-spec"), 60 | m4: thirdPartyRoot 61 | }; -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.waitUntilDone = waitUntilDone; 7 | var defaultTimeoutCsmith = exports.defaultTimeoutCsmith = 5 * 60 * 1000; 8 | var defaultTimeoutSpec = exports.defaultTimeoutSpec = 5 * 60 * 1000; 9 | var defaultTimeoutTranspiler = exports.defaultTimeoutTranspiler = 10 * 60 * 1000; 10 | 11 | function waitUntilDone(proc) { 12 | var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, 13 | getOutput = _ref.getOutput; 14 | 15 | return new Promise(function (resolve, reject) { 16 | if (getOutput) { 17 | proc.stdout.on("data", function (data) { 18 | getOutput.stdout += data.toString(); 19 | }); 20 | proc.stderr.on("data", function (data) { 21 | getOutput.stderr += data.toString(); 22 | }); 23 | } 24 | proc.on("exit", function (code) { 25 | if (code !== 0) { 26 | return reject(new Error("Command terminated with exit code " + code + "\n" + proc.spawnargs.join(" "))); 27 | } 28 | resolve(); 29 | }); 30 | proc.on("error", function (err) { 31 | return reject(err); 32 | }); 33 | }); 34 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasm-exprgen-scripts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "wasm-exprgen": "cli.js" 8 | }, 9 | "scripts": { 10 | "build": "babel src --out-dir lib", 11 | "build-tools": "node cli.js build", 12 | "gen": "node cli.js gen", 13 | "lint": "eslint src" 14 | }, 15 | "author": "Michael Ferris ", 16 | "license": "MIT", 17 | "dependencies": { 18 | "babel-polyfill": "^6.20.0", 19 | "bluebird": "^3.4.6", 20 | "execa": "^0.7.0", 21 | "fs-extra": "^1.0.0", 22 | "klaw": "^2.1.0", 23 | "which": "^1.2.12", 24 | "yargs": "^6.5.0" 25 | }, 26 | "devDependencies": { 27 | "babel-cli": "^6.18.0", 28 | "babel-plugin-transform-object-rest-spread": "^6.20.2", 29 | "babel-preset-latest": "^6.16.0", 30 | "eslint": "^3.3.1", 31 | "eslint-config-cellule": "^4.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/build.js: -------------------------------------------------------------------------------- 1 | import {buildDirectory, binDirectory, outputDir, thirdParties} from "./init"; 2 | import path from "path"; 3 | import fs from "fs-extra"; 4 | import klaw from "klaw"; 5 | import { 6 | csmithDependencies, 7 | llvmDependencies, 8 | emscriptenDependencies, 9 | specInterpreterDependencies 10 | } from "./dependencies"; 11 | import {execFileAsync, spawn} from "child_process"; 12 | import {waitUntilDone} from "./utils"; 13 | import generate from "./generate"; 14 | 15 | async function buildCSmith() { 16 | const {msbuild, cmake, m4} = await csmithDependencies(); 17 | const csmithBuildDir = buildDirectory.csmith; 18 | await fs.ensureDirAsync(csmithBuildDir); 19 | console.log("Starting CSmith build"); 20 | const csmithPath = thirdParties.csmith; 21 | 22 | await execFileAsync(cmake, [ 23 | csmithPath 24 | ], { 25 | cwd: csmithBuildDir, 26 | env: { 27 | path: `${path.dirname(msbuild)};${path.dirname(m4)};${process.env.path}` 28 | } 29 | }); 30 | 31 | if (msbuild) { 32 | const vcproj = path.join(csmithBuildDir, "csmith.sln"); 33 | await execFileAsync(msbuild, [vcproj, "/p:Configuration=Release"]); 34 | 35 | const output = path.join(binDirectory.csmith, "csmith.exe"); 36 | console.log(`CSmith output: ${output}`); 37 | } 38 | } 39 | 40 | async function buildLLVM() { 41 | const clangPath = thirdParties.clang; 42 | const clangDestPath = path.join(thirdParties.llvm, "tools/clang"); 43 | console.log(`Copying ${clangPath} => ${clangDestPath}`); 44 | await fs.copyAsync(clangPath, clangDestPath); 45 | 46 | const { 47 | cmake, 48 | msbuild, 49 | make 50 | } = await llvmDependencies(); 51 | console.log("Starting LLVM build. This might take a while..."); 52 | 53 | const buildDir = buildDirectory.llvm; 54 | await fs.ensureDirAsync(buildDir); 55 | 56 | const llvmPath = thirdParties.llvm; 57 | console.log("Running cmake"); 58 | await execFileAsync(cmake, [ 59 | llvmPath, 60 | "-DCMAKE_BUILD_TYPE=Release", 61 | "-DLLVM_TARGETS_TO_BUILD=X86;JSBackend", 62 | "-DLLVM_INCLUDE_EXAMPLES=OFF", 63 | "-DLLVM_INCLUDE_TESTS=OFF", 64 | "-DCLANG_INCLUDE_EXAMPLES=OFF", 65 | "-DCLANG_INCLUDE_TESTS=OFF", 66 | ], { 67 | cwd: buildDir, 68 | env: { 69 | path: `${path.dirname(msbuild)};${process.env.path}` 70 | } 71 | }); 72 | 73 | if (msbuild) { 74 | // Remove /nologo lines 75 | await (new Promise((resolve, reject) => 76 | klaw(buildDir) 77 | .on("data", item => { 78 | if (path.extname(item.path) === ".vcxproj") { 79 | const content = fs.readFileSync(item.path) 80 | .toString() 81 | .split("\n") 82 | .filter(line => line.indexOf("nologo") === -1) 83 | .join("\n"); 84 | fs.writeFileSync(item.path, content); 85 | } 86 | }) 87 | .on("end", resolve) 88 | .on("error", reject) 89 | )); 90 | console.log("Running msbuild"); 91 | const solution = path.join(buildDir, "LLVM.sln"); 92 | const proc = spawn(msbuild, [solution, "/p:Configuration=Release"], { 93 | stdio: "inherit" 94 | }); 95 | await waitUntilDone(proc); 96 | } else { 97 | console.log("Running make"); 98 | const proc = spawn(make, ["-j4"], { 99 | cwd: buildDir, 100 | stdio: "inherit" 101 | }); 102 | await waitUntilDone(proc); 103 | } 104 | console.log(`LLVM output: ${binDirectory.llvm}`); 105 | } 106 | 107 | export async function prepareEmscriptenConfig() { 108 | const {python} = await emscriptenDependencies(); 109 | const cleanPath = p => p.replace(/\\/g, "/"); 110 | const tmpDir = path.join(outputDir, "tmp"); 111 | const file = ` 112 | EMSCRIPTEN_ROOT = '${cleanPath(thirdParties.emscripten)}' 113 | LLVM_ROOT= '${cleanPath(binDirectory.llvm)}' 114 | PYTHON = '${cleanPath(python)}' 115 | NODE_JS= '${cleanPath(process.argv[0])}' 116 | TEMP_DIR = '${cleanPath(tmpDir)}' 117 | COMPILER_ENGINE = NODE_JS 118 | JS_ENGINES = [NODE_JS] 119 | `; 120 | await fs.ensureDirAsync(tmpDir); 121 | const emscriptenFile = path.join(outputDir, ".emscripten"); 122 | await fs.outputFileAsync(emscriptenFile, file); 123 | console.log(`Generated config file ${emscriptenFile}`); 124 | } 125 | 126 | async function buildSpecInterpreter() { 127 | console.log("Starting Spec interpreter build"); 128 | await fs.ensureDirAsync(buildDirectory.spec); 129 | const interpreterPath = path.join(thirdParties.spec, "interpreter"); 130 | const {cmd, make} = await specInterpreterDependencies(); 131 | if (cmd) { 132 | const proc = spawn(cmd, ["/c", "winmake"], { 133 | cwd: interpreterPath 134 | }); 135 | await waitUntilDone(proc); 136 | await fs.copyAsync(path.join(interpreterPath, "main", "main.d.byte"), path.join(buildDirectory.spec, "wasm.exe")); 137 | } else { 138 | const proc = spawn(make, [], { 139 | cwd: interpreterPath 140 | }); 141 | await waitUntilDone(proc); 142 | await fs.copyAsync(path.join(interpreterPath, "wasm"), path.join(buildDirectory.spec, "wasm")); 143 | } 144 | console.log(`Wasm Spec output: ${binDirectory.spec}`); 145 | } 146 | 147 | export default function build() { 148 | return buildSpecInterpreter() 149 | .catch(err => console.error("Error while building WebAssembly interpreter, validation will not be available\n" + err)) 150 | .then(buildCSmith) 151 | .then(buildLLVM) 152 | .then(prepareEmscriptenConfig) 153 | // Do one generation to trigger binaryen's build 154 | .then(() => generate() 155 | // It is possible to fail to generate a test file here, that doesn't mean the build failed... 156 | .catch(err => console.error(err)) 157 | ); 158 | } 159 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | import yargs from "yargs"; 2 | import generate, {sourceTypes, compileFromSource} from "./generate"; 3 | import {outputDir} from "./init"; 4 | import build, {prepareEmscriptenConfig} from "./build"; 5 | import { 6 | defaultTimeoutCsmith, 7 | defaultTimeoutSpec, 8 | defaultTimeoutTranspiler 9 | } from "./utils"; 10 | 11 | const commonGenOptions = { 12 | outdir: { 13 | alias: "o", 14 | describe: "Output directory for the generated files", 15 | default: outputDir 16 | }, 17 | validate: { 18 | boolean: true, 19 | describe: "Run wasm-spec interpreter to validate generated file", 20 | default: true 21 | }, 22 | interpreted: { 23 | boolean: true, 24 | describe: "Generate interpreted version for comparison purposes", 25 | default: true 26 | }, 27 | inline: { 28 | boolean: true, 29 | describe: "Inline the WebAssembly module in the javascript file" 30 | }, 31 | silent: { 32 | alias: "s", 33 | boolean: true, 34 | describe: "Silently run generating tools" 35 | }, 36 | "timeout-em": { 37 | number: true, 38 | describe: "Timeout for emscripten", 39 | default: defaultTimeoutTranspiler 40 | }, 41 | "timeout-spec": { 42 | number: true, 43 | describe: "Timeout for spec interpreter", 44 | default: defaultTimeoutSpec 45 | } 46 | }; 47 | 48 | yargs 49 | .usage() 50 | .help() 51 | .alias("h", "help") 52 | .wrap(Math.min(yargs.terminalWidth() - 1, 120)) 53 | .command({ 54 | command: "config", 55 | desc: "Updates Emscripten configuration file for this machine", 56 | handler() { 57 | prepareEmscriptenConfig() 58 | .then(() => console.log("Configuration completed")) 59 | .catch(err => { 60 | console.error(err); 61 | process.exit(1); 62 | }); 63 | } 64 | }) 65 | .command({ 66 | command: "build", 67 | desc: "Build the tools needed", 68 | handler() { 69 | build() 70 | .then(() => console.log("Build completed")) 71 | .catch(err => { 72 | console.error(err); 73 | process.exit(1); 74 | }); 75 | } 76 | }) 77 | .command({ 78 | command: "gen", 79 | desc: "generate a random WebAssembly test file", 80 | builder: (_yargs) => _yargs 81 | .options({ 82 | ...commonGenOptions, 83 | "timeout-csmith": { 84 | number: true, 85 | describe: "Timeout for csmith", 86 | default: defaultTimeoutCsmith 87 | }, 88 | attempts: { 89 | alias: "a", 90 | number: true, 91 | describe: "Number of attempts to generate a file, 0 for infinite", 92 | default: 1, 93 | }, 94 | type: { 95 | alias: "t", 96 | string: true, 97 | describe: "Type of source file generated", 98 | choices: ["c", "cpp", "cpp11"], 99 | default: "c" 100 | }, 101 | fileName: { 102 | string: true, 103 | describe: "Name of the generated files (without extension)", 104 | default: "test" 105 | } 106 | }), 107 | handler: (argv) => { 108 | const args = { 109 | sourceType: sourceTypes[argv.type], 110 | validate: argv.validate, 111 | fileName: argv.fileName, 112 | outdir: argv.outdir, 113 | inlineWasm: argv.inline, 114 | timeoutSpec: argv.timeoutSpec, 115 | timeoutCsmith: argv.timeoutCsmith, 116 | timeoutTranspiler: argv.timeoutEm, 117 | execOptions: {}, 118 | }; 119 | if (argv.silent) { 120 | args.execOptions.stdio = "ignore"; 121 | } 122 | doGenerate(generate, argv.attempts, args) 123 | .catch(() => process.exit(1)); 124 | } 125 | }) 126 | .command({ 127 | command: "regen", 128 | desc: "generate WebAssembly from a .c source file", 129 | builder: (_yargs) => _yargs 130 | .options({ 131 | ...commonGenOptions, 132 | source: { 133 | required: true, 134 | string: true, 135 | describe: "Source file to regenerate" 136 | }, 137 | emargs: { 138 | string: true, 139 | describe: "Space separated extra arguments to pass to emcc" 140 | } 141 | }), 142 | handler: (argv) => { 143 | const args = { 144 | validate: argv.validate, 145 | sourceFile: argv.source, 146 | outdir: argv.outdir, 147 | inlineWasm: argv.inline, 148 | timeoutSpec: argv.timeoutSpec, 149 | timeoutTranspiler: argv.timeoutEm, 150 | execOptions: {}, 151 | emOptions: (argv.emargs || "").split(" ") 152 | }; 153 | if (argv.silent) { 154 | args.execOptions.stdio = "ignore"; 155 | } 156 | doGenerate(compileFromSource, 1, args) 157 | .catch(() => process.exit(1)); 158 | } 159 | }) 160 | .demand(1) 161 | .argv; 162 | 163 | async function doGenerate(genFn, maxAttempts, args) { 164 | let nAttempts = 0; 165 | let lastError = null; 166 | while (maxAttempts === 0 || nAttempts < maxAttempts) { 167 | nAttempts++; 168 | try { 169 | const {wasm, src, js, valid} = await genFn(args); 170 | console.log(`Source file: ${src}`); 171 | console.log(`Javascript file: ${js}`); 172 | console.log(`WebAssembly file: ${wasm}${args.validate ? ` is ${valid ? "" : "not "}valid` : ""}`); 173 | return; 174 | } catch (e) { 175 | console.log("Failed to generate test"); 176 | console.error(e); 177 | lastError = e; 178 | } 179 | } 180 | throw lastError; 181 | } 182 | -------------------------------------------------------------------------------- /src/dependencies.js: -------------------------------------------------------------------------------- 1 | import {binDirectory, outputDir, thirdParties, isWindows} from "./init"; 2 | import path from "path"; 3 | import fs from "fs-extra"; 4 | import execa from "execa"; 5 | import which from "which"; 6 | 7 | async function fromPath(bin, opt = {}) { 8 | try { 9 | const rPath = await which.async(bin, opt); 10 | return rPath; 11 | } catch (e) { 12 | if (e.message.indexOf("not found") === -1) { 13 | throw e; 14 | } 15 | } 16 | return null; 17 | } 18 | 19 | let msbuildCache; 20 | async function searchForMsBuild() { 21 | if (!isWindows) { 22 | return null; 23 | } 24 | if (msbuildCache) { 25 | return msbuildCache; 26 | } 27 | const cache = p => { 28 | msbuildCache = p; 29 | console.log(`MsBuild path: ${p}`); 30 | return msbuildCache; 31 | }; 32 | 33 | const getPaths = () => { 34 | const dev15Paths = ["15.0"].map(version => { 35 | const binFolders = ["bin", "bin/x86", "bin/amd64"]; 36 | const vsVersions = ["2017", "Preview"]; 37 | const roots = [ 38 | path.resolve(process.env.ProgramFiles, "Microsoft Visual Studio"), 39 | path.resolve(process.env["ProgramFiles(x86)"], "Microsoft Visual Studio"), 40 | ]; 41 | const allPaths = []; 42 | for (const root of roots) { 43 | for (const vsVersion of vsVersions) { 44 | const folder = path.join(root, vsVersion); 45 | try { 46 | const content = fs.readdirSync(folder); 47 | for (const dir of content) { 48 | for (const binFolder of binFolders) { 49 | allPaths.push(path.join(folder, dir, "msbuild", version, binFolder)); 50 | } 51 | } 52 | } catch (e) { 53 | // ignore 54 | } 55 | } 56 | } 57 | return allPaths.join(";"); 58 | }).join(";"); 59 | 60 | const oldDevPaths = ["14.0", "12.0", "10.0"].map(version => [ 61 | path.resolve(process.env.ProgramFiles, "msbuild", version, "bin/x86"), 62 | path.resolve(process.env["ProgramFiles(x86)"], "msbuild", version, "bin"), 63 | path.resolve(process.env["ProgramFiles(x86)"], "msbuild", version, "bin/amd64"), 64 | ].join(";")).join(";"); 65 | return dev15Paths + oldDevPaths; 66 | }; 67 | const rPath = (await fromPath("msbuild.exe")) || (await fromPath("msbuild.exe", {path: getPaths()})); 68 | if (rPath) { 69 | return cache(rPath); 70 | } 71 | throw new Error("MsBuild is missing"); 72 | } 73 | 74 | export async function csmithDependencies() { 75 | const dependencies = { 76 | msbuild: await searchForMsBuild(), 77 | cmake: await which.async("cmake"), 78 | }; 79 | if (isWindows) { 80 | dependencies.m4 = path.join(thirdParties.m4, "m4.exe"); 81 | } else { 82 | dependencies.m4 = await which.async("m4"); 83 | } 84 | return dependencies; 85 | } 86 | 87 | export async function llvmDependencies() { 88 | const dependencies = { 89 | cmake: await which.async("cmake"), 90 | msbuild: await searchForMsBuild(), 91 | make: isWindows ? null : await which.async("make"), 92 | }; 93 | return dependencies; 94 | } 95 | 96 | async function validatePythonVersion(python) { 97 | if (python) { 98 | try { 99 | const info = await execa(python, ["--version"], { 100 | timeout: 30000 101 | }); 102 | try { 103 | const version = parseFloat(/python (\d+\.\d+)/ig.exec(info.stdout + info.stderr)[1]); 104 | if (version >= 2.7 && version < 3) { 105 | return python; 106 | } 107 | } catch (e) { 108 | // ignore 109 | } 110 | } catch (e) { 111 | console.error(e); 112 | } 113 | } 114 | return null; 115 | } 116 | let pythonCache; 117 | async function searchPython() { 118 | if (pythonCache) { 119 | return pythonCache; 120 | } 121 | 122 | const locations = [ 123 | undefined, // Use current path 124 | process.env.PYTHON, 125 | ]; 126 | if (isWindows) { 127 | locations.push("C:\\Python27"); 128 | } 129 | for (const loc of locations) { 130 | for (const bin of ["python", "python2"]) { 131 | const python = await validatePythonVersion(await fromPath(bin, {path: loc})); 132 | if (python) { 133 | pythonCache = python; 134 | console.log(`Python path: ${python}`); 135 | return python; 136 | } 137 | } 138 | } 139 | throw new Error("Python 2 is missing"); 140 | } 141 | let emCache; 142 | export async function emscriptenDependencies() { 143 | emCache = emCache || await emscriptenDependenciesInternal(); 144 | return emCache; 145 | } 146 | async function emscriptenDependenciesInternal() { 147 | const python = await searchPython(); 148 | const emscriptenRoot = thirdParties.emscripten; 149 | const emcc = path.join(emscriptenRoot, "emcc.py"); 150 | const empp = path.join(emscriptenRoot, "em++.py"); 151 | const runFn = bin => (args = [], opt = {}) => { 152 | const proc = execa(python, [bin, ...args], { 153 | stdio: "inherit", 154 | env: { 155 | ...process.env, 156 | // Change home to tell emscripten to use our .emscripten file 157 | HOME: outputDir, 158 | USERPROFILE: outputDir, 159 | }, 160 | ...opt 161 | }); 162 | return proc; 163 | }; 164 | return { 165 | python, 166 | emcc, 167 | empp, 168 | runEmcc: runFn(emcc), 169 | runEmpp: runFn(empp) 170 | }; 171 | } 172 | let specCache; 173 | export async function specInterpreterDependencies() { 174 | specCache = specCache || await specInterpreterDependenciesInternal(); 175 | return specCache; 176 | } 177 | async function specInterpreterDependenciesInternal() { 178 | // todo:: check for ocaml 179 | if (isWindows) { 180 | return { 181 | cmd: "cmd", 182 | }; 183 | } 184 | return { 185 | make: await which.async("make") 186 | }; 187 | } 188 | 189 | let genDepCache; 190 | export async function generateDependencies() { 191 | genDepCache = genDepCache || await generateDependenciesInternal(); 192 | return genDepCache; 193 | } 194 | 195 | async function generateDependenciesInternal() { 196 | const wasmExe = await fromPath("wasm") || await which.async("wasm", {path: binDirectory.spec}); 197 | const csmithExe = await fromPath("csmith") || await which.async("csmith", {path: binDirectory.csmith}); 198 | const clangExe = await fromPath("clang") || await which.async("clang", {path: binDirectory.llvm}); 199 | 200 | return { 201 | wasm: wasmExe, 202 | csmith: csmithExe, 203 | clang: clangExe, 204 | ...(await emscriptenDependencies()) 205 | }; 206 | } 207 | -------------------------------------------------------------------------------- /src/generate.js: -------------------------------------------------------------------------------- 1 | import {outputDir as defaultOutdir, buildDirectory, thirdParties} from "./init"; 2 | import fs from "fs-extra"; 3 | import path from "path"; 4 | import {generateDependencies} from "./dependencies"; 5 | import execa from "execa"; 6 | import { 7 | defaultTimeoutCsmith, 8 | defaultTimeoutSpec, 9 | defaultTimeoutTranspiler 10 | } from "./utils"; 11 | 12 | export const sourceTypes = { 13 | c: Symbol(), 14 | cpp: Symbol(), 15 | cpp11: Symbol() 16 | }; 17 | 18 | export default async function generate({ 19 | sourceType = sourceTypes.c, 20 | fileName = "test", 21 | outdir = defaultOutdir, 22 | execOptions = {}, 23 | timeoutCsmith = defaultTimeoutCsmith, 24 | ...args 25 | } = {}) { 26 | const {csmith, wasm, runEmcc, runEmpp} = await generateDependencies(); 27 | await fs.ensureDirAsync(outdir); 28 | 29 | // Generate random c file 30 | let sourceFile = path.resolve(outdir, fileName); 31 | const csmithArgs = []; 32 | let transpiler; 33 | switch (sourceType) { 34 | case sourceTypes.c: 35 | sourceFile += ".c"; 36 | transpiler = runEmcc; 37 | break; 38 | case sourceTypes.cpp11: 39 | csmithArgs.push("--cpp11"); 40 | // Fallthrough 41 | case sourceTypes.cpp: 42 | sourceFile += ".cpp"; 43 | csmithArgs.push("--lang-cpp"); 44 | transpiler = runEmpp; 45 | break; 46 | default: throw new Error("Unknown source type"); 47 | } 48 | const csmithProc = execa(csmith, ["-o", sourceFile, ...csmithArgs], { 49 | stdio: "inherit", 50 | cwd: outdir, 51 | ...execOptions, 52 | timeout: timeoutCsmith, 53 | }); 54 | await csmithProc; 55 | const randomOptions = getRandomEmscriptenOptions(); 56 | return compile({ 57 | ...args, 58 | sourceFile, 59 | fileName, 60 | outdir, 61 | execOptions, 62 | emOptions: randomOptions, 63 | transpiler, 64 | specInterpreter: wasm, 65 | }); 66 | } 67 | 68 | export async function compileFromSource(config) { 69 | const {wasm, runEmcc} = await generateDependencies(); 70 | 71 | return compile({ 72 | ...config, 73 | transpiler: runEmcc, 74 | specInterpreter: wasm, 75 | }); 76 | } 77 | 78 | async function compile({ 79 | sourceFile, 80 | validate = true, 81 | interpreted = true, 82 | fileName = "test", 83 | outdir = defaultOutdir, 84 | inlineWasm = false, 85 | execOptions = {}, 86 | timeoutSpec = defaultTimeoutSpec, 87 | timeoutTranspiler = defaultTimeoutTranspiler, 88 | emOptions = [], 89 | transpiler, 90 | specInterpreter 91 | } = {}) { 92 | await fs.ensureDirAsync(outdir); 93 | let shouldInlineWasm = inlineWasm; 94 | // Generate wasm version 95 | const jsFile = path.resolve(outdir, `${fileName}.js`); 96 | const clangFlags = [ 97 | "-w", // disable all diagnostic 98 | ]; 99 | const isCpp11 = sourceFile.endsWith(".cpp"); 100 | if (isCpp11) { 101 | clangFlags.push( 102 | "-std=c++11", 103 | "-Wno-c++11-narrowing" 104 | ); 105 | } 106 | await transpiler([ 107 | sourceFile, 108 | `-I${path.join(thirdParties.csmith, "runtime")}`, 109 | `-I${path.join(buildDirectory.csmith, "runtime")}`, 110 | "-s", "WASM=1", 111 | "-s", `BINARYEN_METHOD='native-wasm${interpreted ? ",interpret-binary" : ""}'`, 112 | "-s", "BINARYEN_ASYNC_COMPILATION=0", 113 | "-g", 114 | ...clangFlags, 115 | ...emOptions, 116 | "-o", jsFile 117 | ], {cwd: outdir, ...execOptions, timeout: timeoutTranspiler}); 118 | const wasmFile = path.resolve(outdir, `${fileName}.wasm`); 119 | const wastFile = path.resolve(outdir, `${fileName}.wast`); 120 | 121 | // Make sure it is valid 122 | // Emscripten sometimes generates invalid wasm file, should investigate 123 | let isValid = true; 124 | let wasmSpecOutput = null; 125 | if (validate && specInterpreter) { 126 | try { 127 | wasmSpecOutput = await execa(specInterpreter, [wasmFile, "-d"], { 128 | cwd: outdir, 129 | ...execOptions, 130 | timeout: timeoutSpec, 131 | }); 132 | } catch (e) { 133 | if (e.timedOut || e.killed) { 134 | // Ignore spec interpreter result if it timed out 135 | console.warn("Spec interpreter timed out"); 136 | } else { 137 | wasmSpecOutput = {stdout: e.stdout, stderr: e.stderr}; 138 | isValid = false; 139 | shouldInlineWasm = true; 140 | const match = /0x[0-9a-f]+:(.+)$/mi.exec(wasmSpecOutput.stderr); 141 | const specErrorMessage = match && match[1] || wasmSpecOutput.stdout + wasmSpecOutput.stderr; 142 | const newJsFile = ` 143 | // check environment for wasm regen 144 | var ENVIRONMENT_IS_NODE = typeof require !== "undefined"; 145 | if (ENVIRONMENT_IS_NODE) { 146 | Module["arguments"] = process.argv.slice(1); 147 | } 148 | 149 | // {{PRE_RUN_ADDITIONS}} 150 | if (WebAssembly.validate(Module["readBinary"]())) { 151 | throw new Error(\`Fatal error: Expected binary to be invalid because: ${specErrorMessage}\`); 152 | } 153 | // {{POST_RUN_ADDITIONS}} 154 | `; 155 | await fs.writeFileAsync(jsFile, newJsFile); 156 | } 157 | } 158 | } else if (validate) { 159 | console.warn("WebAssembly spec interpreter is missing, unable to do validation"); 160 | } 161 | 162 | let oldFile = (await fs.readFileAsync(jsFile)).toString(); 163 | const fd = await fs.openAsync(jsFile, "w"); 164 | await fs.writeAsync(fd, `// Generated with options: ${emOptions.join(" ")}\n\n`); 165 | if (shouldInlineWasm) { 166 | const wasmBuffer = await fs.readFileAsync(wasmFile, "binary"); 167 | let string = ""; 168 | for (let i = 0; i < wasmBuffer.length; ++i) { 169 | string += `\\x${wasmBuffer.charCodeAt(i).toString(16).padStart(2, "0")}`; 170 | } 171 | const buf = ` 172 | // {{PRE_WASM_EXPRGEN}} 173 | var Module = Module || {}; 174 | var wasmBuffer = "${string}"; 175 | Module["readBinary"] = function(file) { 176 | var buffer = new ArrayBuffer(wasmBuffer.length); 177 | var view = new Uint8Array(buffer); 178 | for (var i = 0; i < wasmBuffer.length; ++i) { 179 | view[i] = wasmBuffer.charCodeAt(i); 180 | } 181 | return view; 182 | } 183 | \n\n// {{POST_WASM_EXPRGEN}}\n\n 184 | `; 185 | await fs.writeAsync(fd, buf); 186 | 187 | const nodeRegenOption = 188 | ` 189 | if (ENVIRONMENT_IS_NODE && Module["arguments"].indexOf("--regen-wasm") !== -1) { 190 | var wasmBinaryFile = Module['wasmBinaryFile'] || "${path.basename(wasmFile)}"; 191 | var bin = Module["readBinary"](wasmBinaryFile); 192 | var wstream = require("fs").createWriteStream(wasmBinaryFile, {defaultEncoding: "binary"}); 193 | wstream.write(Buffer.from(bin.buffer)); 194 | wstream.end(); 195 | wstream.on("finish", function() { 196 | console.log("WebAssembly binary buffer written to " + wasmBinaryFile); 197 | }); 198 | } else { 199 | `; 200 | oldFile = oldFile.replace("{{PRE_RUN_ADDITIONS}}", `{{PRE_RUN_ADDITIONS}}\n${nodeRegenOption}`); 201 | oldFile = oldFile.replace("// {{POST_RUN_ADDITIONS}}", "}\n// {{POST_RUN_ADDITIONS}}"); 202 | } 203 | await fs.writeAsync(fd, oldFile); 204 | await fs.closeAsync(fd); 205 | 206 | return { 207 | src: sourceFile, 208 | js: jsFile, 209 | wasm: wasmFile, 210 | wast: wastFile, 211 | valid: isValid, 212 | wasmSpecOutput 213 | }; 214 | } 215 | 216 | function getRandomEmscriptenOptions() { 217 | // Returns a random integer between min (included) and max (excluded) 218 | // Using Math.round() will give you a non-uniform distribution! 219 | function getRandomInt(_max, _min = 0) { 220 | const min = Math.ceil(_min); 221 | const max = Math.floor(_max) + 1; 222 | return Math.floor(Math.random() * (max - min)) + min; 223 | } 224 | // odds: chance to be true out of 10 225 | function roll(odds, max = 10) { 226 | return getRandomInt(max) < odds; 227 | } 228 | const optimizationLevel = getRandomInt(5); 229 | const options = [ 230 | `-O${optimizationLevel < 4 ? optimizationLevel : optimizationLevel < 5 ? "s" : "z"}`, 231 | "-s", "RETAIN_COMPILER_SETTINGS=1" 232 | ]; 233 | 234 | let changedDefaultMem = false; 235 | let totalStack = 5 * 1024 * 1024; 236 | if (roll(3)) { 237 | changedDefaultMem = true; 238 | totalStack = getRandomInt(20, 1) * 1024 * 1024; 239 | options.push("-s", `TOTAL_STACK=${totalStack}`); 240 | } 241 | 242 | const doMemGrowth = roll(3); 243 | const wasmPageSize = 65536; 244 | if (doMemGrowth) { 245 | options.push("-s", "ALLOW_MEMORY_GROWTH=1"); 246 | } 247 | 248 | let totalMem = 256 * wasmPageSize; 249 | if (!changedDefaultMem) { 250 | const changeTotalMem = roll(5); 251 | if (changeTotalMem) { 252 | changedDefaultMem = true; 253 | totalMem = (wasmPageSize * getRandomInt(16000)) | 0; 254 | } 255 | } 256 | 257 | if (changedDefaultMem) { 258 | var totalMemory = wasmPageSize; 259 | while (totalMemory < totalMem || totalMemory < 2 * totalStack) { 260 | if (totalMemory < 16 * 1024 * 1024) { 261 | totalMemory *= 2; 262 | } else { 263 | totalMemory += 16 * 1024 * 1024; 264 | } 265 | } 266 | options.push("-s", `TOTAL_MEMORY=${totalMemory >>> 0}`); 267 | } 268 | 269 | /* Currently not supported for WebAssembly 270 | if (roll(2)) { 271 | let splitMem = getRandomInt(totalMem, totalStack); 272 | splitMem = 1 << Math.log2(splitMem); 273 | if (splitMem < totalStack) { 274 | splitMem *= 2; 275 | } 276 | if (splitMem <= totalMem) { 277 | options.push("-s", `SPLIT_MEMORY=${splitMem}`); 278 | } 279 | } 280 | */ 281 | 282 | if (roll(1, 20)) { 283 | options.push("-s", `GLOBAL_BASE=${getRandomInt(1024)}`); 284 | } 285 | 286 | if (roll(1, 20)) { 287 | options.push("-s", `STACK_START=${getRandomInt(1024)}`); 288 | } 289 | 290 | if (roll(1, 20)) { 291 | options.push("-s", "DOUBLE_MODE=0"); 292 | } 293 | 294 | if (roll(2)) { 295 | options.push("-s", `PRECISE_I64_MATH=${getRandomInt(2)}`); 296 | } 297 | 298 | if (roll(2)) { 299 | options.push("-s", "AGGRESSIVE_VARIABLE_ELIMINATION=1"); 300 | } 301 | 302 | if (roll(2)) { 303 | options.push("-s", `EMULATED_FUNCTION_POINTERS=${getRandomInt(2, 1)}`); 304 | } else if (roll(2)) { 305 | options.push("-s", "EMULATE_FUNCTION_POINTER_CASTS=1"); 306 | } 307 | 308 | return options; 309 | } 310 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import generate, {compileFromSource} from "./generate"; 2 | 3 | export { 4 | generate, 5 | compileFromSource as regen 6 | }; 7 | -------------------------------------------------------------------------------- /src/init.js: -------------------------------------------------------------------------------- 1 | import Promise from "bluebird"; 2 | import fs from "fs-extra"; 3 | import which from "which"; 4 | import childProcess from "child_process"; 5 | import path from "path"; 6 | import os from "os"; 7 | 8 | export const isWindows = os.platform() === "win32"; 9 | 10 | Promise.promisifyAll(fs); 11 | which.async = Promise.promisify(which); 12 | Promise.promisifyAll(childProcess); 13 | 14 | export const rootDir = path.join(__dirname, ".."); 15 | export const toolsDirectory = path.join(rootDir, "tools"); 16 | export const buildDirectory = { 17 | csmith: path.join(toolsDirectory, "csmith"), 18 | spec: path.join(toolsDirectory, "spec"), 19 | llvm: path.join(toolsDirectory, "llvm") 20 | }; 21 | export const binDirectory = { 22 | csmith: path.join(buildDirectory.csmith, "src", "Release"), 23 | spec: buildDirectory.spec, 24 | llvm: path.join(buildDirectory.llvm, isWindows ? "Release/bin" : "bin") 25 | }; 26 | export const outputDir = path.join(rootDir, "output"); 27 | export const thirdPartyRoot = path.join(rootDir, "third_party"); 28 | export const thirdParties = { 29 | llvm: path.join(thirdPartyRoot, "emscripten-fastcomp"), 30 | emscripten: path.join(thirdPartyRoot, "emscripten"), 31 | csmith: path.join(thirdPartyRoot, "csmith"), 32 | clang: path.join(thirdPartyRoot, "clang"), 33 | spec: path.join(thirdPartyRoot, "wasm-spec"), 34 | m4: thirdPartyRoot 35 | }; 36 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | 2 | export const defaultTimeoutCsmith = 5 * 60 * 1000; 3 | export const defaultTimeoutSpec = 5 * 60 * 1000; 4 | export const defaultTimeoutTranspiler = 10 * 60 * 1000; 5 | 6 | export function waitUntilDone(proc, {getOutput} = {}) { 7 | return new Promise((resolve, reject) => { 8 | if (getOutput) { 9 | proc.stdout.on("data", data => {getOutput.stdout += data.toString();}); 10 | proc.stderr.on("data", data => {getOutput.stderr += data.toString();}); 11 | } 12 | proc.on("exit", code => { 13 | if (code !== 0) { 14 | return reject(new Error(`Command terminated with exit code ${code}\n${proc.spawnargs.join(" ")}`)); 15 | } 16 | resolve(); 17 | }); 18 | proc.on("error", err => reject(err)); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /third_party/m4.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cellule/wasm-exprgen/19383c652ad31060108d62f25c952af9811bbe44/third_party/m4.exe -------------------------------------------------------------------------------- /third_party/regex2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cellule/wasm-exprgen/19383c652ad31060108d62f25c952af9811bbe44/third_party/regex2.dll -------------------------------------------------------------------------------- /updateThirdParties.cmd: -------------------------------------------------------------------------------- 1 | pushd %~dp0 2 | call git submodule update --init 3 | cd third_party\clang 4 | call git fetch 5 | call git clean -fdx 6 | call git reset --hard origin/incoming 7 | cd ..\emscripten 8 | call git fetch 9 | call git clean -fdx 10 | call git reset --hard origin/incoming 11 | cd ..\emscripten-fastcomp 12 | call git fetch 13 | call git clean -fdx 14 | call git reset --hard origin/incoming 15 | cd ..\csmith 16 | call git fetch 17 | call git clean -fdx 18 | call git reset --hard origin/master 19 | cd ..\wasm-spec 20 | call git fetch 21 | call git clean -fdx 22 | call git reset --hard origin/master 23 | popd --------------------------------------------------------------------------------