Repository: mattgodbolt/compiler-explorer
Pull request #1802 (2020-01-22 05:01:40)

Async :allthethings:
Hi all!

First up: THIS IS A HUGE PR! I honestly don't expect a line-by-line review, but this is a big one and I appreciate any skim-read/random selection reading.

Most of the changes were mechanical, but while I was there I took the time to rephrase a few bits.

This also bumps the minimum node version to 11 (to use some `Array` builtins instead of always using underscore.js)

Overall I'm pleased with the results: note that I have not got rid of every `.then()`/`.catch` type thing. Specifically the handlers are still mostly traditional as I think a more general solution is probably for the best (to avoid ever hanging a request).

Any time you spend appreciated: I'll be testing and will look to merge in a week or so (unless y'all think this is perfect as-is...)

Thanks!


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f8b6abbf9..7aa27542f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,7 +14,7 @@ Please be forgiving of that, and be ready to help in improving that.
 **Compiler Explorer** targets the latest [Node.js](https://nodejs.org/) LTS,
  so it's better if you do so as well when testing your changes locally.
 
-Nonetheless, it _should_ run in everything post-Node.js 8. [Contact us] if
+Nonetheless, it _should_ run in everything post-Node.js 11. [Contact us] if
  this is not the case for you.
 
 ## In brief
diff --git a/app.js b/app.js
index 5bf12c34f..188b1cfa3 100755
--- a/app.js
+++ b/app.js
@@ -293,356 +293,357 @@ function setupStaticMiddleware(router) {
     };
 }
 
-const awsProps = props.propsFor("aws");
+async function loadSources() {
+    const sourcesDir = "lib/sources";
+    return (await fs.readdir(sourcesDir))
+        .filter(file => file.match(/.*\.js$/))
+        .map(file => require("./" + path.join(sourcesDir, file)));
+}
 
-aws.initConfig(awsProps)
-    .then(initialiseWine)
-    .then(() => {
-        // function to load internal binaries (i.e. lib/source/*.js)
-        function loadSources() {
-            const sourcesDir = "lib/sources";
-            return fs.readdirSync(sourcesDir)
-                .filter(file => file.match(/.*\.js$/))
-                .map(file => require("./" + path.join(sourcesDir, file)));
-        }
+function shouldRedactRequestData(data) {
+    try {
+        const parsed = JSON.parse(data);
+        return !parsed['allowStoreCodeDebug'];
+    } catch (e) {
+        return true;
+    }
+}
 
-        const fileSources = loadSources();
-        const ClientOptionsHandler = require('./lib/options-handler');
-        const clientOptionsHandler = new ClientOptionsHandler(fileSources, compilerProps, defArgs);
-        const CompilationEnvironment = require('./lib/compilation-env');
-        const compilationEnvironment = new CompilationEnvironment(compilerProps, defArgs.doCache);
-        const CompileHandler = require('./lib/handlers/compile').Handler;
-        const compileHandler = new CompileHandler(compilationEnvironment, awsProps);
-        const StorageHandler = require('./lib/storage/storage');
-        const storageHandler = StorageHandler.storageFactory(storageSolution, compilerProps, awsProps, httpRoot);
-        const SourceHandler = require('./lib/handlers/source').Handler;
-        const sourceHandler = new SourceHandler(fileSources, staticHeaders);
-        const CompilerFinder = require('./lib/compiler-finder');
-        const compilerFinder = new CompilerFinder(compileHandler, compilerProps, awsProps, defArgs,
-            clientOptionsHandler);
-        const googleShortUrlResolver = new google.ShortLinkResolver();
-
-        function oldGoogleUrlHandler(req, res, next) {
-            const bits = req.url.split("/");
-            if (bits.length !== 2 || req.method !== "GET") return next();
-            const googleUrl = `https://goo.gl/${encodeURIComponent(bits[1])}`;
-            googleShortUrlResolver.resolve(googleUrl)
-                .then(resultObj => {
-                    const parsed = url.parse(resultObj.longUrl);
-                    const allowedRe = new RegExp(ceProps('allowedShortUrlHostRe'));
-                    if (parsed.host.match(allowedRe) === null) {
-                        logger.warn(`Denied access to short URL ${bits[1]} - linked to ${resultObj.longUrl}`);
-                        return next();
-                    }
-                    res.writeHead(301, {
-                        Location: resultObj.longUrl,
-                        'Cache-Control': 'public'
-                    });
-                    res.end();
-                })
-                .catch(e => {
-                    logger.error(`Failed to expand ${googleUrl} - ${e}`);
-                    next();
-                });
+const googleShortUrlResolver = new google.ShortLinkResolver();
+
+function oldGoogleUrlHandler(req, res, next) {
+    const bits = req.url.split("/");
+    if (bits.length !== 2 || req.method !== "GET") return next();
+    const googleUrl = `https://goo.gl/${encodeURIComponent(bits[1])}`;
+    googleShortUrlResolver.resolve(googleUrl)
+        .then(resultObj => {
+            const parsed = url.parse(resultObj.longUrl);
+            const allowedRe = new RegExp(ceProps('allowedShortUrlHostRe'));
+            if (parsed.host.match(allowedRe) === null) {
+                logger.warn(`Denied access to short URL ${bits[1]} - linked to ${resultObj.longUrl}`);
+                return next();
+            }
+            res.writeHead(301, {
+                Location: resultObj.longUrl,
+                'Cache-Control': 'public'
+            });
+            res.end();
+        })
+        .catch(e => {
+            logger.error(`Failed to expand ${googleUrl} - ${e}`);
+            next();
+        });
+}
+
+function startListening(server) {
+    const ss = systemdSocket();
+    let _port;
+    if (ss) {
+        // ms (5 min default)
+        const idleTimeout = process.env.IDLE_TIMEOUT;
+        const timeout = (typeof idleTimeout !== 'undefined' ? idleTimeout : 300) * 1000;
+        if (idleTimeout) {
+            const exit = () => {
+                logger.info("Inactivity timeout reached, exiting.");
+                process.exit(0);
+            };
+            let idleTimer = setTimeout(exit, timeout);
+            const reset = () => {
+                clearTimeout(idleTimer);
+                idleTimer = setTimeout(exit, timeout);
+            };
+            server.all('*', reset);
+            logger.info(`  IDLE_TIMEOUT: ${idleTimeout}`);
         }
+        _port = ss;
+    } else {
+        _port = defArgs.port;
+    }
+    logger.info(`  Listening on http://${defArgs.hostname || 'localhost'}:${_port}/`);
+    logger.info(`  Startup duration: ${new Date() - startTime}ms`);
+    logger.info("=======================================");
+    server.listen(_port, defArgs.hostname);
+}
 
-        function startListening(server) {
-            const ss = systemdSocket();
-            let _port;
-            if (ss) {
-                // ms (5 min default)
-                const idleTimeout = process.env.IDLE_TIMEOUT;
-                const timeout = (typeof idleTimeout !== 'undefined' ? idleTimeout : 300) * 1000;
-                if (idleTimeout) {
-                    let exit = () => {
-                        logger.info("Inactivity timeout reached, exiting.");
-                        process.exit(0);
-                    };
-                    let idleTimer = setTimeout(exit, timeout);
-                    let reset = () => {
-                        clearTimeout(idleTimer);
-                        idleTimer = setTimeout(exit, timeout);
-                    };
-                    server.all('*', reset);
-                    logger.info(`  IDLE_TIMEOUT: ${idleTimeout}`);
-                }
-                _port = ss;
-            } else {
-                _port = defArgs.port;
+function setupSentry(sentryDsn) {
+    if (!sentryDsn) {
+        logger.info("Not configuring sentry");
+        return;
+    }
+    const sentryEnv = ceProps("sentryEnvironment");
+    Sentry.init({
+        dsn: sentryDsn,
+        release: travisBuildNumber,
+        environment: sentryEnv,
+        beforeSend(event) {
+            if (event.request
+                && event.request.data
+                && shouldRedactRequestData(event.request.data)) {
+                event.request.data = JSON.stringify({redacted: true});
             }
-            logger.info(`  Listening on http://${defArgs.hostname || 'localhost'}:${_port}/`);
-            logger.info(`  Startup duration: ${new Date() - startTime}ms`);
-            logger.info("=======================================");
-            server.listen(_port, defArgs.hostname);
+            return event;
         }
+    });
+    logger.info(`Configured with Sentry endpoint ${sentryDsn}`);
+}
 
-        function shouldRedactRequestData(data) {
-            try {
-                const parsed = JSON.parse(data);
-                return !parsed['allowStoreCodeDebug'];
-            } catch (e) {
-                return true;
-            }
+const awsProps = props.propsFor("aws");
+
+// eslint-disable-next-line max-statements
+async function main() {
+    await aws.initConfig(awsProps);
+    await initialiseWine();
+    const fileSources = await loadSources();
+
+    const ClientOptionsHandler = require('./lib/options-handler');
+    const clientOptionsHandler = new ClientOptionsHandler(fileSources, compilerProps, defArgs);
+    const CompilationEnvironment = require('./lib/compilation-env');
+    const compilationEnvironment = new CompilationEnvironment(compilerProps, defArgs.doCache);
+    const CompileHandler = require('./lib/handlers/compile').Handler;
+    const compileHandler = new CompileHandler(compilationEnvironment, awsProps);
+    const StorageHandler = require('./lib/storage/storage');
+    const storageHandler = StorageHandler.storageFactory(storageSolution, compilerProps, awsProps, httpRoot);
+    const SourceHandler = require('./lib/handlers/source').Handler;
+    const sourceHandler = new SourceHandler(fileSources, staticHeaders);
+    const CompilerFinder = require('./lib/compiler-finder');
+    const compilerFinder = new CompilerFinder(compileHandler, compilerProps, awsProps, defArgs, clientOptionsHandler);
+
+    logger.info("=======================================");
+    if (gitReleaseName) logger.info(`  git release ${gitReleaseName}`);
+    if (travisBuildNumber) logger.info(`  travis build ${travisBuildNumber}`);
+
+    const initialFindResults = await compilerFinder.find();
+    const initialCompilers = initialFindResults.compilers;
+    let prevCompilers;
+    if (defArgs.ensureNoCompilerClash) {
+        logger.warn('Ensuring no compiler ids clash');
+        if (initialFindResults.foundClash) {
+            // If we are forced to have no clashes, throw an error with some explanation
+            throw new Error('Clashing compilers in the current environment found!');
+        } else {
+            logger.info('No clashing ids found, continuing normally...');
+        }
+    }
+
+    setupSentry(aws.getConfig('sentryDsn'));
+
+    const webServer = express(),
+        sFavicon = require('serve-favicon'),
+        bodyParser = require('body-parser'),
+        morgan = require('morgan'),
+        compression = require('compression'),
+        router = express.Router(),
+        healthCheck = require('./lib/handlers/health-check');
+
+    const healthCheckFilePath = ceProps("healthCheckFilePath", false);
+
+    const routeApi = new RouteAPI(router, compileHandler, ceProps, storageHandler, renderGoldenLayout);
+
+    function onCompilerChange(compilers) {
+        if (JSON.stringify(prevCompilers) === JSON.stringify(compilers)) {
+            return;
+        }
+        logger.debug("Compilers:", compilers);
+        if (compilers.length === 0) {
+            logger.error("#### No compilers found: no compilation will be done!");
         }
+        prevCompilers = compilers;
+        clientOptionsHandler.setCompilers(compilers);
+        routeApi.apiHandler.setCompilers(compilers);
+        routeApi.apiHandler.setLanguages(languages);
+        routeApi.apiHandler.setOptions(clientOptionsHandler);
+    }
 
-        compilerFinder.find()
-            .then(result => {
-                let compilers = result.compilers;
-                let prevCompilers;
-                if (defArgs.ensureNoCompilerClash) {
-                    logger.warn('Ensuring no compiler ids clash');
-                    if (result.foundClash) {
-                        // If we are forced to have no clashes, throw an error with some explanation
-                        throw new Error('Clashing compilers in the current environment found!');
-                    } else {
-                        logger.info('No clashing ids found, continuing normally...');
-                    }
-                }
-
-                const sentryDsn = aws.getConfig('sentryDsn');
-                if (sentryDsn) {
-                    const sentryEnv = ceProps("sentryEnvironment");
-                    Sentry.init({
-                        dsn: sentryDsn,
-                        release: travisBuildNumber,
-                        environment: sentryEnv,
-                        beforeSend(event) {
-                            if (event.request
-                                && event.request.data
-                                && shouldRedactRequestData(event.request.data)) {
-                                event.request.data = JSON.stringify({redacted: true});
-                            }
-                            return event;
-                        }
-                    });
-                    logger.info(`Configured with Sentry endpoint ${sentryDsn}`);
-                } else {
-                    logger.info("Not configuring sentry");
-                }
-
-                const webServer = express(),
-                    sFavicon = require('serve-favicon'),
-                    bodyParser = require('body-parser'),
-                    morgan = require('morgan'),
-                    compression = require('compression'),
-                    router = express.Router(),
-                    healthCheck = require('./lib/handlers/health-check');
-
-                const healthCheckFilePath = ceProps("healthCheckFilePath", false);
-
-                const routeApi = new RouteAPI(router, compileHandler, ceProps,
-                    storageHandler, renderGoldenLayout);
-
-                function onCompilerChange(compilers) {
-                    if (JSON.stringify(prevCompilers) === JSON.stringify(compilers)) {
-                        return;
-                    }
-                    logger.debug("Compilers:", compilers);
-                    if (compilers.length === 0) {
-                        logger.error("#### No compilers found: no compilation will be done!");
-                    }
-                    prevCompilers = compilers;
-                    clientOptionsHandler.setCompilers(compilers);
-                    routeApi.apiHandler.setCompilers(compilers);
-                    routeApi.apiHandler.setLanguages(languages);
-                    routeApi.apiHandler.setOptions(clientOptionsHandler);
-                }
-
-                onCompilerChange(compilers);
-
-                const rescanCompilerSecs = ceProps('rescanCompilerSecs', 0);
-                if (rescanCompilerSecs) {
-                    logger.info(`Rescanning compilers every ${rescanCompilerSecs} secs`);
-                    setInterval(() => compilerFinder.find().then(result => onCompilerChange(result.compilers)),
-                        rescanCompilerSecs * 1000);
-                }
-
-                const sentrySlowRequestMs = ceProps("sentrySlowRequestMs", 0);
-
-                webServer
-                    .set('trust proxy', true)
-                    .set('view engine', 'pug')
-                    .on('error', err => logger.error('Caught error in web handler; continuing:', err))
-                    // sentry request handler must be the first middleware on the app
-                    .use(Sentry.Handlers.requestHandler())
-                    // eslint-disable-next-line no-unused-vars
-                    .use(responseTime((req, res, time) => {
-                        if (sentrySlowRequestMs > 0 && time >= sentrySlowRequestMs) {
-                            Sentry.withScope(scope => {
-                                scope.setExtra('duration_ms', time);
-                                Sentry.captureMessage('SlowRequest', 'warning');
-                            });
-                        }
-                    }))
-                    // Handle healthchecks at the root, as they're not expected from the outside world
-                    .use('/healthcheck', new healthCheck.HealthCheckHandler(healthCheckFilePath).handle)
-                    .use(httpRoot, router)
-                    .use((req, res, next) => {
-                        next({status: 404, message: `page "${req.path}" could not be found`});
-                    })
-                    // sentry error handler must be the first error handling middleware
-                    .use(Sentry.Handlers.errorHandler)
-                    // eslint-disable-next-line no-unused-vars
-                    .use((err, req, res) => {
-                        const status =
-                            err.status ||
-                            err.statusCode ||
-                            err.status_code ||
-                            (err.output && err.output.statusCode) ||
-                            500;
-                        const message = err.message || 'Internal Server Error';
-                        res.status(status);
-                        res.render('error', renderConfig({error: {code: status, message: message}}));
-                    });
-
-                logger.info("=======================================");
-                if (gitReleaseName) logger.info(`  git release ${gitReleaseName}`);
-                if (travisBuildNumber) logger.info(`  travis build ${travisBuildNumber}`);
-
-                function renderConfig(extra, urlOptions) {
-                    const urlOptionsWhitelist = [
-                        'readOnly', 'hideEditorToolbars'
-                    ];
-                    const filteredUrlOptions = _.mapObject(
-                        _.pick(urlOptions, urlOptionsWhitelist),
-                        val => utils.toProperty(val));
-                    const allExtraOptions = _.extend({}, filteredUrlOptions, extra);
-                    const options = _.extend({}, allExtraOptions, clientOptionsHandler.get());
-                    options.optionsHash = clientOptionsHandler.getHash();
-                    options.compilerExplorerOptions = JSON.stringify(allExtraOptions);
-                    options.extraBodyClass = options.embedded ? 'embedded' : extraBodyClass;
-                    options.httpRoot = httpRoot;
-                    options.staticRoot = staticRoot;
-                    options.storageSolution = storageSolution;
-                    options.require = pugRequireHandler;
-                    return options;
-                }
-
-                function renderGoldenLayout(config, metadata, req, res) {
-                    staticHeaders(res);
-                    contentPolicyHeader(res);
-                    res.render('index', renderConfig({
-                        embedded: false,
-                        config: config,
-                        metadata: metadata
-                    }, req.query));
-                }
-
-                const embeddedHandler = function (req, res) {
-                    staticHeaders(res);
-                    contentPolicyHeader(res);
-                    res.render('embed', renderConfig({embedded: true}, req.query));
-                };
-                if (isDevMode()) {
-                    setupWebPackDevMiddleware(router);
-                } else {
-                    setupStaticMiddleware(router);
-                }
-
-                morgan.token('gdpr_ip', req => utils.anonymizeIp(req.ip));
-
-                // Based on combined format, but: GDPR compliant IP, no timestamp & no unused fields for our usecase
-                const morganFormat = isDevMode() ? 'dev' : ':gdpr_ip ":method :url" :status';
-
-                const shortenerLib = require(`./lib/shortener-${clientOptionsHandler.options.urlShortenService}`);
-                const shortener = shortenerLib({storageHandler});
-
-                /*
-                 * This is a workaround to make cross origin monaco web workers function
-                 * in spite of the monaco webpack plugin hijacking the MonacoEnvironment global.
-                 *
-                 * see https://github.com/microsoft/monaco-editor-webpack-plugin/issues/42
-                 *
-                 * This workaround wouldn't be so bad, if it didn't _also_ rely on *another* bug to
-                 * actually work.
-                 *
-                 * The webpack plugin incorrectly uses
-                 *     window.__webpack_public_path__
-                 * when it should use
-                 *     __webpack_public_path__
-                 *
-                 * see https://github.com/microsoft/monaco-editor-webpack-plugin/pull/63
-                 *
-                 * We can leave __webpack_public_path__ with the correct value, which lets runtime chunk
-                 * loading continue to function correctly.
-                 *
-                 * We can then set window.__webpack_public_path__ to the below handler, which lets us
-                 * fabricate a worker on the fly.
-                 *
-                 * This is bad and I feel bad.
-                 */
-                router.get('/workers/:worker', (req, res) => {
-                    staticHeaders(res);
-                    res.set('Content-Type', 'application/javascript');
-                    res.end(`importScripts('${urljoin(staticRoot, req.params.worker)}');`);
+    onCompilerChange(initialCompilers);
+
+    const rescanCompilerSecs = ceProps('rescanCompilerSecs', 0);
+    if (rescanCompilerSecs) {
+        logger.info(`Rescanning compilers every ${rescanCompilerSecs} secs`);
+        setInterval(() => compilerFinder.find().then(result => onCompilerChange(result.compilers)),
+            rescanCompilerSecs * 1000);
+    }
+
+    const sentrySlowRequestMs = ceProps("sentrySlowRequestMs", 0);
+
+    webServer
+        .set('trust proxy', true)
+        .set('view engine', 'pug')
+        .on('error', err => logger.error('Caught error in web handler; continuing:', err))
+        // sentry request handler must be the first middleware on the app
+        .use(Sentry.Handlers.requestHandler())
+        // eslint-disable-next-line no-unused-vars
+        .use(responseTime((req, res, time) => {
+            if (sentrySlowRequestMs > 0 && time >= sentrySlowRequestMs) {
+                Sentry.withScope(scope => {
+                    scope.setExtra('duration_ms', time);
+                    Sentry.captureMessage('SlowRequest', 'warning');
                 });
+            }
+        }))
+        // Handle healthchecks at the root, as they're not expected from the outside world
+        .use('/healthcheck', new healthCheck.HealthCheckHandler(healthCheckFilePath).handle)
+        .use(httpRoot, router)
+        .use((req, res, next) => {
+            next({status: 404, message: `page "${req.path}" could not be found`});
+        })
+        // sentry error handler must be the first error handling middleware
+        .use(Sentry.Handlers.errorHandler)
+        // eslint-disable-next-line no-unused-vars
+        .use((err, req, res) => {
+            const status =
+                err.status ||
+                err.statusCode ||
+                err.status_code ||
+                (err.output && err.output.statusCode) ||
+                500;
+            const message = err.message || 'Internal Server Error';
+            res.status(status);
+            res.render('error', renderConfig({error: {code: status, message: message}}));
+        });
+
+    function renderConfig(extra, urlOptions) {
+        const urlOptionsWhitelist = [
+            'readOnly', 'hideEditorToolbars'
+        ];
+        const filteredUrlOptions = _.mapObject(
+            _.pick(urlOptions, urlOptionsWhitelist),
+            val => utils.toProperty(val));
+        const allExtraOptions = _.extend({}, filteredUrlOptions, extra);
+        const options = _.extend({}, allExtraOptions, clientOptionsHandler.get());
+        options.optionsHash = clientOptionsHandler.getHash();
+        options.compilerExplorerOptions = JSON.stringify(allExtraOptions);
+        options.extraBodyClass = options.embedded ? 'embedded' : extraBodyClass;
+        options.httpRoot = httpRoot;
+        options.staticRoot = staticRoot;
+        options.storageSolution = storageSolution;
+        options.require = pugRequireHandler;
+        return options;
+    }
 
-                router
-                    .use(morgan(morganFormat, {
-                        stream: logger.stream,
-                        // Skip for non errors (2xx, 3xx)
-                        skip: (req, res) => res.statusCode >= 400
-                    }))
-                    .use(morgan(morganFormat, {
-                        stream: logger.warnStream,
-                        // Skip for non user errors (4xx)
-                        skip: (req, res) => res.statusCode < 400 || res.statusCode >= 500
-                    }))
-                    .use(morgan(morganFormat, {
-                        stream: logger.errStream,
-                        // Skip for non server errors (5xx)
-                        skip: (req, res) => res.statusCode < 500
-                    }))
-                    .use(compression())
-                    .get('/', (req, res) => {
-                        staticHeaders(res);
-                        contentPolicyHeader(res);
-                        res.render('index', renderConfig({embedded: false}, req.query));
-                    })
-                    .get('/e', embeddedHandler)
-                    // legacy. not a 301 to prevent any redirect loops between old e links and embed.html
-                    .get('/embed.html', embeddedHandler)
-                    .get('/embed-ro', (req, res) => {
-                        staticHeaders(res);
-                        contentPolicyHeader(res);
-                        res.render('embed', renderConfig({embedded: true, readOnly: true}, req.query));
-                    })
-                    .get('/robots.txt', (req, res) => {
-                        staticHeaders(res);
-                        res.end('User-agent: *\nSitemap: https://godbolt.org/sitemap.xml\nDisallow:');
-                    })
-                    .get('/sitemap.xml', (req, res) => {
-                        staticHeaders(res);
-                        res.set('Content-Type', 'application/xml');
-                        res.render('sitemap');
-                    })
-                    .use(sFavicon(path.resolve(__dirname, 'static', 'favicon.ico')))
-                    .get('/client-options.js', (req, res) => {
-                        staticHeaders(res);
-                        res.set('Content-Type', 'application/javascript');
-                        const options = JSON.stringify(clientOptionsHandler.get());
-                        res.end(`window.compilerExplorerOptions = ${options};`);
-                    })
-                    .use(bodyParser.json({limit: ceProps('bodyParserLimit', maxUploadSize)}))
-                    .use(bodyParser.text({limit: ceProps('bodyParserLimit', maxUploadSize), type: () => true}))
-                    .use('/source', sourceHandler.handle.bind(sourceHandler))
-                    .use('/g', oldGoogleUrlHandler)
-                    .post('/shortener', shortener);
-
-                routeApi.InitializeRoutes();
-
-                if (!defArgs.doCache) {
-                    logger.info("  with disabled caching");
-                }
-                setupEventLoopLagLogging();
-                startListening(webServer);
-            })
-            .catch(err => {
-                logger.error("Promise error (shutting down):", err);
-                process.exit(1);
-            });
+    function renderGoldenLayout(config, metadata, req, res) {
+        staticHeaders(res);
+        contentPolicyHeader(res);
+        res.render('index', renderConfig({
+            embedded: false,
+            config: config,
+            metadata: metadata
+        }, req.query));
+    }
+
+    const embeddedHandler = function (req, res) {
+        staticHeaders(res);
+        contentPolicyHeader(res);
+        res.render('embed', renderConfig({embedded: true}, req.query));
+    };
+    if (isDevMode()) {
+        setupWebPackDevMiddleware(router);
+    } else {
+        setupStaticMiddleware(router);
+    }
+
+    morgan.token('gdpr_ip', req => utils.anonymizeIp(req.ip));
+
+    // Based on combined format, but: GDPR compliant IP, no timestamp & no unused fields for our usecase
+    const morganFormat = isDevMode() ? 'dev' : ':gdpr_ip ":method :url" :status';
+
+    const shortenerLib = require(`./lib/shortener-${clientOptionsHandler.options.urlShortenService}`);
+    const shortener = shortenerLib({storageHandler});
+
+    /*
+     * This is a workaround to make cross origin monaco web workers function
+     * in spite of the monaco webpack plugin hijacking the MonacoEnvironment global.
+     *
+     * see https://github.com/microsoft/monaco-editor-webpack-plugin/issues/42
+     *
+     * This workaround wouldn't be so bad, if it didn't _also_ rely on *another* bug to
+     * actually work.
+     *
+     * The webpack plugin incorrectly uses
+     *     window.__webpack_public_path__
+     * when it should use
+     *     __webpack_public_path__
+     *
+     * see https://github.com/microsoft/monaco-editor-webpack-plugin/pull/63
+     *
+     * We can leave __webpack_public_path__ with the correct value, which lets runtime chunk
+     * loading continue to function correctly.
+     *
+     * We can then set window.__webpack_public_path__ to the below handler, which lets us
+     * fabricate a worker on the fly.
+     *
+     * This is bad and I feel bad.
+     */
+    router.get('/workers/:worker', (req, res) => {
+        staticHeaders(res);
+        res.set('Content-Type', 'application/javascript');
+        res.end(`importScripts('${urljoin(staticRoot, req.params.worker)}');`);
+    });
+
+    router
+        .use(morgan(morganFormat, {
+            stream: logger.stream,
+            // Skip for non errors (2xx, 3xx)
+            skip: (req, res) => res.statusCode >= 400
+        }))
+        .use(morgan(morganFormat, {
+            stream: logger.warnStream,
+            // Skip for non user errors (4xx)
+            skip: (req, res) => res.statusCode < 400 || res.statusCode >= 500
+        }))
+        .use(morgan(morganFormat, {
+            stream: logger.errStream,
+            // Skip for non server errors (5xx)
+            skip: (req, res) => res.statusCode < 500
+        }))
+        .use(compression())
+        .get('/', (req, res) => {
+            staticHeaders(res);
+            contentPolicyHeader(res);
+            res.render('index', renderConfig({embedded: false}, req.query));
+        })
+        .get('/e', embeddedHandler)
+        // legacy. not a 301 to prevent any redirect loops between old e links and embed.html
+        .get('/embed.html', embeddedHandler)
+        .get('/embed-ro', (req, res) => {
+            staticHeaders(res);
+            contentPolicyHeader(res);
+            res.render('embed', renderConfig({embedded: true, readOnly: true}, req.query));
+        })
+        .get('/robots.txt', (req, res) => {
+            staticHeaders(res);
+            res.end('User-agent: *\nSitemap: https://godbolt.org/sitemap.xml\nDisallow:');
+        })
+        .get('/sitemap.xml', (req, res) => {
+            staticHeaders(res);
+            res.set('Content-Type', 'application/xml');
+            res.render('sitemap');
+        })
+        .use(sFavicon(path.resolve(__dirname, 'static', 'favicon.ico')))
+        .get('/client-options.js', (req, res) => {
+            staticHeaders(res);
+            res.set('Content-Type', 'application/javascript');
+            const options = JSON.stringify(clientOptionsHandler.get());
+            res.end(`window.compilerExplorerOptions = ${options};`);
+        })
+        .use(bodyParser.json({limit: ceProps('bodyParserLimit', maxUploadSize)}))
+        .use(bodyParser.text({limit: ceProps('bodyParserLimit', maxUploadSize), type: () => true}))
+        .use('/source', sourceHandler.handle.bind(sourceHandler))
+        .use('/g', oldGoogleUrlHandler)
+        .post('/shortener', shortener);
+
+    routeApi.InitializeRoutes();
+
+    if (!defArgs.doCache) {
+        logger.info("  with disabled caching");
+    }
+    setupEventLoopLagLogging();
+    startListening(webServer);
+}
+
+main()
+    .then(() => {
     })
     .catch(err => {
-        logger.error("AWS Init Promise error (shutting down)", err);
+        logger.error("Top-level error (shutting down):", err);
         process.exit(1);
     });
diff --git a/etc/scripts/find-node b/etc/scripts/find-node
index d057f7735..189e4bc0e 100755
--- a/etc/scripts/find-node
+++ b/etc/scripts/find-node
@@ -24,7 +24,7 @@ find_node() {
 
 NODE=$(find_node)
 
-NODE_MIN_VERSION=8
+NODE_MIN_VERSION=11
 NODE_VERSION_USED=12
 NODE_VERSION=$(${NODE} --version)
 NODE_MAJOR_VERSION=$(echo ${NODE_VERSION} | cut -f1 -d. | sed 's/^v//g')
diff --git a/lib/aws.js b/lib/aws.js
index 474e5e0ce..441c713b1 100644
--- a/lib/aws.js
+++ b/lib/aws.js
@@ -34,19 +34,13 @@ class InstanceFetcher {
         this.tagValue = properties('tagValue');
     }
 
-    onInstances(result) {
-        let allInstances = [];
-        result.Reservations.forEach(res => allInstances = allInstances.concat(res.Instances));
-        return allInstances.filter(reservation => {
-            if (reservation.State.Name !== "running") return false;
-            return reservation.Tags.some(t => t.Key === this.tagKey && t.Value === this.tagValue);
-        });
-    }
-
-    getInstances() {
-        return this.ec2.describeInstances()
-            .promise()
-            .then(result => this.onInstances(result));
+    async getInstances() {
+        return (await this.ec2.describeInstances().promise()).Reservations
+            .flatMap(r => r.Instances)
+            .filter(reservation => {
+                if (reservation.State.Name !== "running") return false;
+                return reservation.Tags.some(t => t.Key === this.tagKey && t.Value === this.tagValue);
+            });
     }
 }
 
@@ -54,35 +48,29 @@ let awsConfigInit = false;
 let awsConfig = {};
 let awsProps = null;
 
-function loadAwsConfig(properties) {
+async function loadAwsConfig(properties) {
     let region = properties('region');
-    if (!region) {
-        return Promise.resolve({});
-    }
-    let ssm = new AWS.SSM({region: region});
+    if (!region) return {};
+    const ssm = new AWS.SSM({region: region});
     const path = '/compiler-explorer/';
-    return ssm.getParametersByPath({
-        Path: path
-    })
-        .promise()
-        .then((response) => {
-            const map = {};
-            response.Parameters.forEach((response) => {
-                map[response.Name.substr(path.length)] = response.Value;
-            });
-            logger.info("AWS info:", map);
-            return map;
-        })
-        .catch((error) => {
-            logger.error(`Failed to get AWS info: ${error}`);
-            return {};
+    try {
+        const response = await ssm.getParametersByPath({Path: path}).promise();
+        const map = {};
+        response.Parameters.forEach((response) => {
+            map[response.Name.substr(path.length)] = response.Value;
         });
+        logger.info("AWS info:", map);
+        return map;
+    } catch (err) {
+        logger.error(`Failed to get AWS info: ${err}`);
+        return {};
+    }
 }
 
-function initConfig(properties) {
+async function initConfig(properties) {
     awsConfigInit = true;
     awsProps = properties;
-    return loadAwsConfig(properties).then(map => awsConfig = map);
+    awsConfig = await loadAwsConfig(properties);
 }
 
 function getConfig(name) {
diff --git a/lib/base-compiler.js b/lib/base-compiler.js
index 84e48e445..5d0c912f1 100644
--- a/lib/base-compiler.js
+++ b/lib/base-compiler.js
@@ -72,6 +72,8 @@ class BaseCompiler {
         }
         this.outputFilebase = "output";
 
+        this.mtime = null;
+
         this.packager = new packager();
     }
 
@@ -161,28 +163,29 @@ class BaseCompiler {
         return result;
     }
 
-    execBinary(executable, maxSize, executeParameters) {
+    async execBinary(executable, maxSize, executeParameters) {
         // We might want to save this in the compilation environment once execution is made available
         const timeoutMs = this.env.ceProps('binaryExecTimeoutMs', 2000);
-        return exec.sandbox(executable, executeParameters.args, {
-            maxOutput: maxSize,
-            timeoutMs: timeoutMs,
-            ldPath: _.union(this.compiler.ldPath, executeParameters.ldPath).join(":"),
-            input: executeParameters.stdin
-        })  // TODO make config
-            .then(execResult => {
-                execResult.stdout = utils.parseOutput(execResult.stdout);
-                execResult.stderr = utils.parseOutput(execResult.stderr);
-                return execResult;
-            }).catch(err => {
-                // TODO: is this the best way? Perhaps failures in sandbox shouldn't reject
-                // with "results", but instead should play on?
-                return {
-                    stdout: err.stdout ? utils.parseOutput(err.stdout) : [],
-                    stderr: err.stderr ? utils.parseOutput(err.stderr) : [],
-                    code: err.code !== undefined ? err.code : -1
-                };
+        try {
+            // TODO make config
+            const execResult = await exec.sandbox(executable, executeParameters.args, {
+                maxOutput: maxSize,
+                timeoutMs: timeoutMs,
+                ldPath: _.union(this.compiler.ldPath, executeParameters.ldPath).join(":"),
+                input: executeParameters.stdin
             });
+            execResult.stdout = utils.parseOutput(execResult.stdout);
+            execResult.stderr = utils.parseOutput(execResult.stderr);
+            return execResult;
+        } catch (err) {
+            // TODO: is this the best way? Perhaps failures in sandbox shouldn't reject
+            // with "results", but instead should play on?
+            return {
+                stdout: err.stdout ? utils.parseOutput(err.stdout) : [],
+                stderr: err.stderr ? utils.parseOutput(err.stderr) : [],
+                code: err.code !== undefined ? err.code : -1
+            };
+        }
     }
 
     filename(fn) {
@@ -202,8 +205,7 @@ class BaseCompiler {
         const foundLib = _.find(this.compiler.libs, (o, libId) => libId === selectedLib.id);
         if (!foundLib) return false;
 
-        const foundVersion = _.find(foundLib.versions, (o, versionId) => versionId === selectedLib.version);
-        return foundVersion;
+        return _.find(foundLib.versions, (o, versionId) => versionId === selectedLib.version);
     }
 
     findAutodetectStaticLibLink(linkname) {
@@ -528,17 +530,20 @@ class BaseCompiler {
         const compilationResultFilename = "compilation-result.json";
         try {
             const outputFilename = await this.env.executableGet(key, dirPath);
-            logger.debug(`Using cached package ${outputFilename}`);
-            await this.packager.unpack(outputFilename, dirPath);
-            const buildResults = JSON.parse(await fs.readFile(path.join(dirPath, compilationResultFilename)));
-            return Object.assign({}, buildResults, {
-                code: 0,
-                inputFilename: path.join(dirPath, this.compileFilename),
-                dirPath: dirPath,
-                executableFilename: this.getExecutableFilename(dirPath, this.outputFilebase)
-            });
+            if (outputFilename) {
+                logger.debug(`Using cached package ${outputFilename}`);
+                await this.packager.unpack(outputFilename, dirPath);
+                const buildResults = JSON.parse(await fs.readFile(path.join(dirPath, compilationResultFilename)));
+                return Object.assign({}, buildResults, {
+                    code: 0,
+                    inputFilename: path.join(dirPath, this.compileFilename),
+                    dirPath: dirPath,
+                    executableFilename: this.getExecutableFilename(dirPath, this.outputFilebase)
+                });
+            }
+            logger.debug("Tried to get executable from cache, but got a cache miss");
         } catch (err) {
-            logger.debug("Tried to get executable from cache, but got an error: ", {err});
+            logger.error("Tried to get executable from cache, but got an error:", {err});
         }
         const compilationResult = await this.buildExecutableInFolder(key, dirPath);
         if (compilationResult.code !== 0) {
@@ -791,30 +796,31 @@ class BaseCompiler {
         return demangler.process(result);
     }
 
-    processOptOutput(hasOptOutput, optPath) {
-        let output = [];
-        return new Promise(resolve => {
-            fs.createReadStream(optPath, {encoding: "utf-8"})
-                .pipe(new compilerOptInfo.LLVMOptTransformer())
-                .on("data", opt => {
-                    if (opt.DebugLoc && opt.DebugLoc.File && opt.DebugLoc.File.indexOf(this.compileFilename) > -1) {
-                        output.push(opt);
-                    }
-                })
-                .on("end", () => {
-                    if (this.compiler.demangler) {
-                        const result = JSON.stringify(output, null, 4);
-                        this.exec(this.compiler.demangler, ["-n", "-p"], {input: result})
-                            .then(demangleResult => resolve(JSON.parse(demangleResult.stdout)))
-                            .catch(exception => {
-                                logger.warn(`Caught exception ${exception} during opt demangle parsing`);
-                                resolve(output);
-                            });
-                    } else {
-                        resolve(output);
-                    }
-                });
-        });
+    async processOptOutput(optPath) {
+        const output = [];
+
+        const optStream = fs.createReadStream(optPath, {encoding: "utf-8"})
+            .pipe(new compilerOptInfo.LLVMOptTransformer());
+
+        for await (const opt of optStream) {
+            if (opt.DebugLoc && opt.DebugLoc.File && opt.DebugLoc.File.indexOf(this.compileFilename) > -1) {
+                output.push(opt);
+            }
+        }
+
+        if (this.compiler.demangler) {
+            const result = JSON.stringify(output, null, 4);
+            try {
+                const demangleResult = await this.exec(
+                    this.compiler.demangler, ["-n", "-p"], {input: result});
+                return JSON.parse(demangleResult.stdout);
+            } catch (exception) {
+                // swallow exception and return non-demangled output
+                logger.warn(`Caught exception ${exception} during opt demangle parsing`);
+            }
+        }
+
+        return output;
     }
 
     couldSupportASTDump(version) {
@@ -957,7 +963,7 @@ Please select another pass or change filters.`;
     async postProcess(result, outputFilename, filters) {
         const postProcess = _.compact(this.compiler.postProcess);
         const maxSize = this.env.ceProps("max-asm-size", 64 * 1024 * 1024);
-        const optPromise = result.hasOptOutput ? this.processOptOutput(result.hasOptOutput, result.optPath) : "";
+        const optPromise = result.hasOptOutput ? this.processOptOutput(result.optPath) : "";
         const asmPromise = (filters.binary && this.supportsObjdump())
             ? this.objdump(outputFilename, result, maxSize, filters.intel, filters.demangle)
             : (async () => {
@@ -1031,35 +1037,38 @@ Please select another pass or change filters.`;
         return this.execCompilerCached(this.compiler.exe, [versionFlag], execOptions);
     }
 
-    initialise() {
-        if (this.getRemote()) return Promise.resolve(this);
+    async initialise(mtime) {
+        this.mtime = mtime;
+        if (this.getRemote()) return this;
         const compiler = this.compiler.exe;
         const versionRe = new RegExp(this.compiler.versionRe || '.*', 'i');
-        return this.env.enqueue(() => {
-            return this.getVersion();
-        }).then(result => {
-            if (result.code !== 0) {
-                logger.warn(`Compiler '${compiler}' - non-zero result ${result.code}`);
-            }
-            let version = "";
-            _.each(utils.splitLines(result.stdout + result.stderr), line => {
-                if (version) return;
-                const match = line.match(versionRe);
-                if (match) version = match[0];
-            });
-            if (!version) {
-                logger.error(`Unable to find compiler version for '${compiler}' with re ${versionRe}:`, result);
+        const result = await this.env.enqueue(() => {
+            try {
+                return this.getVersion();
+            } catch (err) {
+                logger.error(`Unable to get version for compiler '${compiler}' - ${err}`);
                 return null;
             }
-            logger.debug(`${compiler} is version '${version}'`);
-            this.compiler.version = version;
-            this.compiler.supportsCfg = this.isCfgCompiler(version);
-            this.compiler.supportsAstView = this.couldSupportASTDump(version);
-            return this.getArgumentParser().parse(this);
-        }, err => {
-            logger.error(`Unable to get version for compiler '${compiler}' - ${err}`);
-            return null;
         });
+        if (!result) return null;
+        if (result.code !== 0) {
+            logger.warn(`Compiler '${compiler}' - non-zero result ${result.code}`);
+        }
+        let version = "";
+        _.each(utils.splitLines(result.stdout + result.stderr), line => {
+            if (version) return;
+            const match = line.match(versionRe);
+            if (match) version = match[0];
+        });
+        if (!version) {
+            logger.error(`Unable to find compiler version for '${compiler}' with re ${versionRe}:`, result);
+            return null;
+        }
+        logger.debug(`${compiler} is version '${version}'`);
+        this.compiler.version = version;
+        this.compiler.supportsCfg = this.isCfgCompiler(version);
+        this.compiler.supportsAstView = this.couldSupportASTDump(version);
+        return this.getArgumentParser().parse(this);
     }
 
     getInfo() {
diff --git a/lib/cache/base.js b/lib/cache/base.js
index 1928e6614..e638d55a4 100644
--- a/lib/cache/base.js
+++ b/lib/cache/base.js
@@ -53,18 +53,14 @@ class BaseCache {
         return getHash(object, HashVersion);
     }
 
-    get(key) {
+    async get(key) {
         this.gets++;
-        return this.getInternal(key)
-            .then(result => {
-                if (result.hit) {
-                    this.hits++;
-                }
-                return result;
-            });
+        const result = await this.getInternal(key);
+        if (result.hit) this.hits++;
+        return result;
     }
 
-    put(key, value, creator) {
+    async put(key, value, creator) {
         if (!(value instanceof Buffer))
             value = Buffer.from(value);
         this.puts++;
@@ -72,13 +68,13 @@ class BaseCache {
     }
 
     // eslint-disable-next-line no-unused-vars
-    getInternal(key) {
-        return Promise.reject("should be implemented in subclass");
+    async getInternal(key) {
+        throw new Error("should be implemented in subclass");
     }
 
     // eslint-disable-next-line no-unused-vars
-    putInternal(key, value, creator) {
-        return Promise.reject("should be implemented in subclass");
+    async putInternal(key, value, creator) {
+        throw new Error("should be implemented in subclass");
     }
 }
 
diff --git a/lib/cache/multi.js b/lib/cache/multi.js
index 1dc709d02..3c1949bde 100644
--- a/lib/cache/multi.js
+++ b/lib/cache/multi.js
@@ -37,19 +37,14 @@ class MultiCache extends BaseCache {
         return `${super.statString()}. ${this.upstream.map(c => `${c.name}: ${c.statString()}`).join(". ")}`;
     }
 
-    getInternal(key) {
-        let promiseChain = Promise.resolve({hit: false});
-        for (let cache of this.upstream) {
-            promiseChain = promiseChain.then(upstream => {
-                if (upstream.hit) {
-                    // TODO: could propagate hits to lower-level caches. But that's a lot of work and it's not
-                    // clear to me it is better than just serving from the lowest-level cache.
-                    return upstream;
-                }
-                return cache.get(key);
-            });
+    async getInternal(key) {
+        for (const cache of this.upstream) {
+            const result = await cache.get(key);
+            // TODO: could propagate hits to lower-level caches. But that's a lot of work and it's not
+            // clear to me it is better than just serving from the lowest-level cache.
+            if (result.hit) return result;
         }
-        return promiseChain;
+        return {hit: false};
     }
 
     putInternal(object, value, creator) {
diff --git a/lib/compilation-env.js b/lib/compilation-env.js
index 9257c13de..df106d430 100644
--- a/lib/compilation-env.js
+++ b/lib/compilation-env.js
@@ -77,18 +77,16 @@ class CompilationEnvironment {
         return env;
     }
 
-    cacheGet(object) {
-        const key = BaseCache.hash(object);
-        return this.cache.get(key).then((result) => {
-            if ((this.cache.gets % this.reportCacheEvery) === 0) {
-                this.cache.report();
-            }
-            if (!result.hit) return null;
-            return JSON.parse(result.data);
-        });
+    async cacheGet(object) {
+        const result = await this.cache.get(BaseCache.hash(object));
+        if ((this.cache.gets % this.reportCacheEvery) === 0) {
+            this.cache.report();
+        }
+        if (!result.hit) return null;
+        return JSON.parse(result.data);
     }
 
-    cachePut(object, result, creator) {
+    async cachePut(object, result, creator) {
         const key = BaseCache.hash(object);
         return this.cache.put(key, JSON.stringify(result), creator);
     }
@@ -96,31 +94,22 @@ class CompilationEnvironment {
     async compilerCacheGet(object) {
         const key = BaseCache.hash(object);
         const result = await this.compilerCache.get(key);
-        if (!result.hit) {
-            return null;
-        }
+        if (!result.hit) return null;
         return JSON.parse(result.data);
     }
 
-    compilerCachePut(object, result, creator) {
+    async compilerCachePut(object, result, creator) {
         const key = BaseCache.hash(object);
         return this.compilerCache.put(key, JSON.stringify(result), creator);
     }
 
-    executableGet(object, destinationFolder) {
+    async executableGet(object, destinationFolder) {
         const key = BaseCache.hash(object) + '_exec';
-        return this.executableCache.get(key).then((result) => {
-            return new Promise((resolve, reject) => {
-                if (!result.hit) reject("Executable not found in cache");
-
-                const filepath = destinationFolder + '/' + key;
-                fs.writeFile(filepath, result.data).then(() => {
-                    resolve(filepath);
-                }).catch((err) => {
-                    reject(err);
-                });
-            });
-        });
+        const result = await this.executableCache.get(key);
+        if (!result.hit) return null;
+        const filepath = destinationFolder + '/' + key;
+        await fs.writeFile(filepath, result.data);
+        return filepath;
     }
 
     executablePut(object, filepath) {
diff --git a/lib/compiler-arguments.js b/lib/compiler-arguments.js
index 024f37a38..bfaff50cc 100644
--- a/lib/compiler-arguments.js
+++ b/lib/compiler-arguments.js
@@ -37,7 +37,7 @@ class CompilerArguments {
         this.storeSpecificArguments = false;
     }
 
-    loadFromStorage(awsProps) {
+    async loadFromStorage(awsProps) {
         const region = awsProps('region');
         const bucket = awsProps('storageBucketArgStats');
         const prefix = awsProps('storagePrefixArgStats');
@@ -45,17 +45,16 @@ class CompilerArguments {
             AWS.config.update({region: region});
 
             const s3 = new S3Bucket(bucket, region);
-            s3.get(this.compilerId + ".json", prefix)
-                .then(result => {
-                    if (result.hit) {
-                        const stats = JSON.parse(result.data.toString());
-                        _.each(stats, (times, arg) => {
-                            this.addOptionToStatistics(arg, times);
-                        });
-                    } else {
-                        logger.debug(`${this.compilerId}.json not present in storage`);
-                    }
+            const result = await s3.get(this.compilerId + ".json", prefix);
+            if (result.hit) {
+                const stats = JSON.parse(result.data.toString());
+                _.each(stats, (times, arg) => {
+                    this.addOptionToStatistics(arg, times);
                 });
+                logger.debug(`${this.compilerId}.json has stats`, stats);
+            } else {
+                logger.debug(`${this.compilerId}.json not present in storage`);
+            }
         }
     }
 
diff --git a/lib/compiler-finder.js b/lib/compiler-finder.js
index e3e4b5525..470eee509 100644
--- a/lib/compiler-finder.js
+++ b/lib/compiler-finder.js
@@ -31,6 +31,8 @@ const logger = require('./logger').logger,
     https = require('https'),
     aws = require('./aws');
 
+const sleep = require('util').promisify(setTimeout);
+
 /***
  * Finds and initializes the compilers stored on the properties files
  */
@@ -57,11 +59,11 @@ class CompilerFinder {
         return this.awsPoller.getInstances();
     }
 
-    fetchRemote(host, port, uriBase, props, langId) {
+    async fetchRemote(host, port, uriBase, props, langId) {
         const requestLib = port === 443 ? https : http;
         const uriSchema = port === 443 ? "https" : "http";
         const uri = urljoin(`${uriSchema}://${host}:${port}`, uriBase);
-        let apiPath = urljoin('/', uriBase || '', 'api/compilers', langId || '');
+        const apiPath = urljoin('/', uriBase || '', 'api/compilers', langId || '');
         logger.info(`Fetching compilers from remote source ${uri}`);
         return this.retryPromise(
             () => {
@@ -134,29 +136,23 @@ class CompilerFinder {
         });
     }
 
-    fetchAws() {
+    async fetchAws() {
         logger.info("Fetching instances from AWS");
-        return this.awsInstances().then(instances => {
-            return Promise.all(instances.map(instance => {
-                logger.info("Checking instance " + instance.InstanceId);
-                let address = instance.PrivateDnsName;
-                if (this.awsProps("externalTestMode", false)) {
-                    address = instance.PublicDnsName;
-                }
-                return this.fetchRemote(address, this.args.port, '', this.awsProps, null);
-            }));
-        });
+        const instances = await this.awsInstances();
+        return Promise.all(instances.map(instance => {
+            logger.info("Checking instance " + instance.InstanceId);
+            const address = this.awsProps("externalTestMode", false) ? instance.PublicDnsName : instance.PrivateDnsName;
+            return this.fetchRemote(address, this.args.port, '', this.awsProps, null);
+        }));
     }
 
-    compilerConfigFor(langId, compilerName, parentProps) {
+    async compilerConfigFor(langId, compilerName, parentProps) {
         const base = `compiler.${compilerName}.`;
 
         function props(propName, def) {
-            let propsForCompiler = parentProps(langId, base + propName, undefined);
-            if (propsForCompiler === undefined) {
-                propsForCompiler = parentProps(langId, propName, def);
-            }
-            return propsForCompiler;
+            const propsForCompiler = parentProps(langId, base + propName, undefined);
+            if (propsForCompiler !== undefined) return propsForCompiler;
+            return parentProps(langId, propName, def);
         }
 
         const ceToolsPath = props("ceToolsPath", "./");
@@ -165,24 +161,22 @@ class CompilerFinder {
         const supportsLibraryCodeFilter = !!props("supportsLibraryCodeFilter", true);
         const group = props("group", "");
         const demanglerProp = props("demangler", "");
-        const demangler = demanglerProp === "" ?
-            "" : path.normalize(demanglerProp.replace("${ceToolsPath}", ceToolsPath));
+        const demangler = demanglerProp ? path.normalize(demanglerProp.replace("${ceToolsPath}", ceToolsPath)) : "";
         const isSemVer = props("isSemVer", false);
         const baseName = props("baseName", null);
         const semverVer = props("semver", "");
         const name = props("name", compilerName);
         const envVars = (() => {
-            let envVarsString = props("envVars", "");
+            const envVarsString = props("envVars", "");
             if (envVarsString === "") {
                 return [];
-            } else {
-                let arr = [];
-                for (const el of envVarsString.split(':')) {
-                    const [env, setting] = el.split('=');
-                    arr.push([env, setting]);
-                }
-                return arr;
             }
+            const arr = [];
+            for (const el of envVarsString.split(':')) {
+                const [env, setting] = el.split('=');
+                arr.push([env, setting]);
+            }
+            return arr;
         })();
         const exe = props("exe", compilerName);
         const exePath = path.dirname(exe);
@@ -226,10 +220,10 @@ class CompilerFinder {
             unwiseOptions: props("unwiseOptions", "").split("|")
         };
         logger.debug("Found compiler", compilerInfo);
-        return Promise.resolve(compilerInfo);
+        return compilerInfo;
     }
 
-    recurseGetCompilers(langId, compilerName, parentProps) {
+    async recurseGetCompilers(langId, compilerName, parentProps) {
         // Don't treat @ in paths as remote addresses if requested
         if (this.args.fetchCompilersFromRemote && compilerName.indexOf("@") !== -1) {
             const bits = compilerName.split("@");
@@ -256,17 +250,16 @@ class CompilerFinder {
         return this.compilerConfigFor(langId, compilerName, parentProps);
     }
 
-    getCompilers() {
-        this.getExes();
-        let compilers = [];
-        _.each(this.exes, (exs, langId) => {
+    async getCompilers() {
+        const compilers = [];
+        _.each(this.getExes(), (exs, langId) => {
             _.each(exs, exe => compilers.push(this.recurseGetCompilers(langId, exe, this.compilerProps)));
         });
-        return compilers;
+        return Promise.all(compilers);
     }
 
     ensureDistinct(compilers) {
-        let ids = {};
+        const ids = {};
         let foundClash = false;
         _.each(compilers, compiler => {
             if (!ids[compiler.id]) ids[compiler.id] = [];
@@ -282,41 +275,34 @@ class CompilerFinder {
         return {compilers, foundClash};
     }
 
-    retryPromise(promiseFunc, name, maxFails, retryMs) {
-        return new Promise(function (resolve, reject) {
-            let fails = 0;
-
-            function doit() {
-                const promise = promiseFunc();
-                promise.then(function (arg) {
-                    resolve(arg);
-                }, function (e) {
-                    fails++;
-                    if (fails < maxFails) {
-                        logger.warn(`Failed ${name} : ${e}, retrying`);
-                        setTimeout(doit, retryMs);
-                    } else {
-                        logger.error(`Too many retries for ${name} : ${e}`);
-                        reject(e);
-                    }
-                });
+    async retryPromise(promiseFunc, name, maxFails, retryMs) {
+        for (let fails = 0; fails < maxFails; ++fails) {
+            try {
+                return await promiseFunc();
+            } catch (e) {
+                if (fails < (maxFails - 1)) {
+                    logger.warn(`Failed ${name} : ${e}, retrying`);
+                    await sleep(retryMs);
+                } else {
+                    logger.error(`Too many retries for ${name} : ${e}`);
+                    throw e;
+                }
             }
-
-            doit();
-        });
+        }
     }
 
     getExes() {
-        this.exes = this.compilerProps(this.languages, "compilers", "", exs => _.compact(exs.split(":")));
-        logger.info('Exes found:', this.exes);
-        this.getNdkExes();
+        const langToCompilers = this.compilerProps(this.languages, "compilers", "", exs => _.compact(exs.split(":")));
+        this.addNdkExes(langToCompilers);
+        logger.info('Exes found:', langToCompilers);
+        return langToCompilers;
     }
 
-    getNdkExes() {
+    addNdkExes(langToCompilers) {
         const ndkPaths = this.compilerProps(this.languages, 'androidNdk');
         _.each(ndkPaths, (ndkPath, langId) => {
             if (ndkPath) {
-                let toolchains = fs.readdirSync(`${ndkPath}/toolchains`);
+                const toolchains = fs.readdirSync(`${ndkPath}/toolchains`);
                 toolchains.forEach((version, index, a) => {
                     const path = `${ndkPath}/toolchains/${version}/prebuilt/linux-x86_64/bin/`;
                     if (fs.existsSync(path)) {
@@ -326,21 +312,16 @@ class CompilerFinder {
                         a[index] = null;
                     }
                 });
-                toolchains = toolchains.filter(x => x !== null);
-                this.exes[langId].push(toolchains);
+                langToCompilers[langId].push(toolchains.filter(x => x !== null));
             }
         });
     }
 
-    find() {
-        return Promise.all(this.getCompilers())
-            .then(_.flatten)
-            .then(compilers => this.compileHandler.setCompilers(compilers))
-            .then(compilers => _.compact(compilers))
-            .then(this.ensureDistinct)
-            .then(result => {
-                return {foundClash: result.foundClash, compilers: _.sortBy(result.compilers, "name")};
-            });
+    async find() {
+        const compilers = (await this.getCompilers()).flat(Infinity);
+        await this.compileHandler.setCompilers(compilers);
+        const result = this.ensureDistinct(_.compact(compilers));
+        return {foundClash: result.foundClash, compilers: _.sortBy(result.compilers, "name")};
     }
 }
 
diff --git a/lib/compilers/ada.js b/lib/compilers/ada.js
index 26f4d7102..51a1f77b5 100644
--- a/lib/compilers/ada.js
+++ b/lib/compilers/ada.js
@@ -61,7 +61,7 @@ class AdaCompiler extends BaseCompiler {
         return source;
     }
 
-    runCompiler(compiler, options, inputFilename, execOptions) {
+    async runCompiler(compiler, options, inputFilename, execOptions) {
         if (!execOptions) {
             execOptions = this.getDefaultExecOptions();
         }
@@ -71,20 +71,18 @@ class AdaCompiler extends BaseCompiler {
         // find where the '-cargs' flag is in options. This is to allow us to set the 
         // output as 'output.s' and not end up with 'example.s'. If the output is left
         // as 'example.s' CE can't find it and thus you get no output.
-        let inputFileName = options.pop();
+        const inputFileName = options.pop();
         for (let i = 0; i < options.length; i++) {
             if (options[i] === '-cargs') {
                 options.splice(i, 0, inputFileName);
                 break;
             }
         }
-        return this.exec(compiler, options, execOptions)
-            .then(result => {
-                result.inputFilename = inputFilename;
-                result.stdout = utils.parseOutput(result.stdout, inputFilename);
-                result.stderr = utils.parseOutput(result.stderr, inputFilename);
-                return result;
-            });
+        const result = await this.exec(compiler, options, execOptions);
+        result.inputFilename = inputFilename;
+        result.stdout = utils.parseOutput(result.stdout, inputFilename);
+        result.stderr = utils.parseOutput(result.stderr, inputFilename);
+        return result;
     }
 }
 
diff --git a/lib/compilers/argument-parsers.js b/lib/compilers/argument-parsers.js
index abd967471..03ddc30c1 100644
--- a/lib/compilers/argument-parsers.js
+++ b/lib/compilers/argument-parsers.js
@@ -65,17 +65,13 @@ class BaseParser {
         return options;
     }
 
-    static getOptions(compiler, helpArg) {
-        return compiler.execCompilerCached(compiler.compiler.exe, [helpArg]).then(result => {
-            let options = {};
-            if (result.code === 0) {
-                const optionFinder = /^\s*(--?[a-z0-9=+,[\]<>|-]*)\s*(.*)/i;
-
-                options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder);
-            }
-            compiler.possibleArguments.populateOptions(options);
-            return options;
-        });
+    static async getOptions(compiler, helpArg) {
+        const optionFinder = /^\s*(--?[a-z0-9=+,[\]<>|-]*)\s*(.*)/i;
+        const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]);
+        const options = result.code === 0
+            ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {};
+        compiler.possibleArguments.populateOptions(options);
+        return options;
     }
 
     static parse(compiler) {
@@ -84,102 +80,93 @@ class BaseParser {
 }
 
 class GCCParser extends BaseParser {
-    static parse(compiler) {
-        return Promise.all([
+    static async parse(compiler) {
+        const results = await Promise.all([
             GCCParser.getOptions(compiler, "--target-help"),
             GCCParser.getOptions(compiler, "--help=common"),
             GCCParser.getOptions(compiler, "--help=optimizers")
-        ]).then(results => {
-            const options = _.extend.apply(_.extend, results);
-            const keys =  _.keys(options);
-            logger.debug(`gcc-like compiler options: ${keys.join(" ")}`);
-            if (BaseParser.hasSupport(options, "-masm=")) {
-                compiler.compiler.intelAsm = "-masm=intel";
-                compiler.compiler.supportsIntel = true;
-            }
-            if (BaseParser.hasSupport(options, "-fdiagnostics-color")) {
-                if (compiler.compiler.options) compiler.compiler.options += " ";
-                compiler.compiler.options += "-fdiagnostics-color=always";
-            }
-            // This check is not infallible, but takes care of Rust and Swift being picked up :)
-            if (_.find(keys, key => key.startsWith('-fdump-'))) {
-                compiler.compiler.supportsGccDump = true;
-            }
-            return compiler;
-        });
+        ]);
+        const options = Object.assign({}, ...results);
+        const keys = _.keys(options);
+        logger.debug(`gcc-like compiler options: ${keys.join(" ")}`);
+        if (BaseParser.hasSupport(options, "-masm=")) {
+            compiler.compiler.intelAsm = "-masm=intel";
+            compiler.compiler.supportsIntel = true;
+        }
+        if (BaseParser.hasSupport(options, "-fdiagnostics-color")) {
+            if (compiler.compiler.options) compiler.compiler.options += " ";
+            compiler.compiler.options += "-fdiagnostics-color=always";
+        }
+        // This check is not infallible, but takes care of Rust and Swift being picked up :)
+        if (_.find(keys, key => key.startsWith('-fdump-'))) {
+            compiler.compiler.supportsGccDump = true;
+        }
+        return compiler;
     }
 }
 
 class ClangParser extends BaseParser {
-    static parse(compiler) {
-        return ClangParser.getOptions(compiler, "--help").then(options => {
-            logger.debug(`clang-like compiler options: ${_.keys(options).join(" ")}`);
-            if (BaseParser.hasSupport(options, '-fsave-optimization-record')) {
-                compiler.compiler.optArg = "-fsave-optimization-record";
-                compiler.compiler.supportsOptOutput = true;
-            }
-            if (BaseParser.hasSupport(options, "-fcolor-diagnostics")) {
-                if (compiler.compiler.options) compiler.compiler.options += " ";
-                compiler.compiler.options += "-fcolor-diagnostics";
-            }
-            if (BaseParser.hasSupport(options, "-emit-llvm")) {
-                compiler.compiler.supportsIrView = true;
-                compiler.compiler.irArg = ['-Xclang', '-emit-llvm', '-fsyntax-only'];
-            }
-            if (BaseParser.hasSupport(options, "-fno-crash-diagnostics")) {
-                if (compiler.compiler.options) compiler.compiler.options += " ";
-                compiler.compiler.options += "-fno-crash-diagnostics";
-            }
-            return compiler;
-        });
+    static async parse(compiler) {
+        const options = await ClangParser.getOptions(compiler, "--help");
+        logger.debug(`clang-like compiler options: ${_.keys(options).join(" ")}`);
+        if (BaseParser.hasSupport(options, '-fsave-optimization-record')) {
+            compiler.compiler.optArg = "-fsave-optimization-record";
+            compiler.compiler.supportsOptOutput = true;
+        }
+        if (BaseParser.hasSupport(options, "-fcolor-diagnostics")) {
+            if (compiler.compiler.options) compiler.compiler.options += " ";
+            compiler.compiler.options += "-fcolor-diagnostics";
+        }
+        if (BaseParser.hasSupport(options, "-emit-llvm")) {
+            compiler.compiler.supportsIrView = true;
+            compiler.compiler.irArg = ['-Xclang', '-emit-llvm', '-fsyntax-only'];
+        }
+        if (BaseParser.hasSupport(options, "-fno-crash-diagnostics")) {
+            if (compiler.compiler.options) compiler.compiler.options += " ";
+            compiler.compiler.options += "-fno-crash-diagnostics";
+        }
+        return compiler;
     }
 }
 
 class PascalParser extends BaseParser {
-    static parse(compiler) {
-        return PascalParser.getOptions(compiler, "-help").then(() => compiler);
+    static async parse(compiler) {
+        await PascalParser.getOptions(compiler, "-help");
+        return compiler;
     }
 }
 
 class ISPCParser extends BaseParser {
-    static parse(compiler) {
-        return ISPCParser.getOptions(compiler, "--help").then(options => {
-            if (BaseParser.hasSupport(options, "--x86-asm-syntax")) {
-                compiler.compiler.intelAsm = "--x86-asm-syntax=intel";
-                compiler.compiler.supportsIntel = true;
-            }
-            return compiler;
-        });
+    static async parse(compiler) {
+        const options = await ISPCParser.getOptions(compiler, "--help");
+        if (BaseParser.hasSupport(options, "--x86-asm-syntax")) {
+            compiler.compiler.intelAsm = "--x86-asm-syntax=intel";
+            compiler.compiler.supportsIntel = true;
+        }
+        return compiler;
     }
 
-    static getOptions(compiler, helpArg) {
-        return compiler.execCompilerCached(compiler.compiler.exe, [helpArg]).then(result => {
-            let options = {};
-            if (result.code === 0) {
-                const optionFinder = /^\s*\[(--?[a-z0-9=+,{}()\s<>/|-]*)\]\s*(.*)/i;
-
-                options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder);
-            }
-            compiler.possibleArguments.populateOptions(options);
-            return options;
-        });
+    static async getOptions(compiler, helpArg) {
+        const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]);
+        const optionFinder = /^\s*\[(--?[a-z0-9=+,{}()\s<>/|-]*)\]\s*(.*)/i;
+        const options = result.code === 0
+            ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {};
+        compiler.possibleArguments.populateOptions(options);
+        return options;
     }
 }
 
 class JavaParser extends BaseParser {
-
-    static parse(compiler) {
-        return JavaParser.getOptions(compiler, "-help").then(() => compiler);
+    static async parse(compiler) {
+        await JavaParser.getOptions(compiler, "-help");
+        return compiler;
     }
 }
 
 class VCParser extends BaseParser {
-    static parse(compiler) {
-        return Promise.all([
-            VCParser.getOptions(compiler, "/help")
-        ]).then(() => {
-            return compiler;
-        });
+    static async parse(compiler) {
+        await VCParser.getOptions(compiler, "/help");
+        return compiler;
     }
 
     static parseLines(stdout, optionRegex) {
@@ -214,7 +201,7 @@ class VCParser extends BaseParser {
                 };
             }
         };
-    
+
         utils.eachLine(stdout, line => {
             if (line.length === 0) return;
             if (line.includes("C/C++ COMPILER OPTIONS")) return;
@@ -237,60 +224,55 @@ class VCParser extends BaseParser {
         return options;
     }
 
-    static getOptions(compiler, helpArg) {
-        return compiler.execCompilerCached(compiler.compiler.exe, [helpArg]).then(result => {
-            let options = {};
-            if (result.code === 0) {
-                const optionFinder = /^\s*(\/[a-z0-9=:+#.,[\]{}<>|_-]*)\s*(.*)/i;
-
-                options = this.parseLines(result.stdout, optionFinder);
-            }
-            compiler.possibleArguments.populateOptions(options);
-            return options;
-        });
+    static async getOptions(compiler, helpArg) {
+        const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]);
+        const optionFinder = /^\s*(\/[a-z0-9=:+#.,[\]{}<>|_-]*)\s*(.*)/i;
+        const options = result.code === 0
+            ? this.parseLines(result.stdout, optionFinder) : {};
+        compiler.possibleArguments.populateOptions(options);
+        return options;
     }
 }
 
+
 class RustParser extends BaseParser {
-    static parse(compiler) {
-        return Promise.all([
+    static async parse(compiler) {
+        const results = await Promise.all([
             RustParser.getOptions(compiler, "--help"),
             RustParser.getOptions(compiler, "-C help"),
             RustParser.getOptions(compiler, "--help -v")
-        ]).then(results => {
-            const options = _.extend.apply(_.extend, results);
-            if (BaseParser.hasSupport(options, "--color")) {
-                if (compiler.compiler.options) compiler.compiler.options += " ";
-                compiler.compiler.options += "--color=always";
-            }
-            return compiler;
-        });
+        ]);
+        const options = Object.assign({}, ...results);
+        if (BaseParser.hasSupport(options, "--color")) {
+            if (compiler.compiler.options) compiler.compiler.options += " ";
+            compiler.compiler.options += "--color=always";
+        }
+        return compiler;
     }
 
-    static getOptions(compiler, helpArg) {
-        return compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')).then(result => {
-            let options = {};
-            if (result.code === 0) {
-                if (helpArg === "-C help") {
-                    const optionFinder = /^\s*(-C\s*[a-z0-9=-]*)\s--\s(.*)/i;
+    static async getOptions(compiler, helpArg) {
+        const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' '));
+        let options = {};
+        if (result.code === 0) {
+            if (helpArg === "-C help") {
+                const optionFinder = /^\s*(-C\s*[a-z0-9=-]*)\s--\s(.*)/i;
 
-                    options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder);
-                } else {
-                    const optionFinder = /^\s*(--?[a-z0-9=+,[\]<>|-]*)\s*(.*)/i;
+                options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder);
+            } else {
+                const optionFinder = /^\s*(--?[a-z0-9=+,[\]<>|-]*)\s*(.*)/i;
 
-                    options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder);
-                }
+                options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder);
             }
-            compiler.possibleArguments.populateOptions(options);
-            return options;
-        });
+        }
+        compiler.possibleArguments.populateOptions(options);
+        return options;
     }
 }
 
 class NimParser extends BaseParser {
-
-    static parse(compiler) {
-        return NimParser.getOptions(compiler, "-help").then(() => compiler);
+    static async parse(compiler) {
+        await NimParser.getOptions(compiler, "-help");
+        return compiler;
     }
 }
 
@@ -303,5 +285,5 @@ module.exports = {
     Pascal: PascalParser,
     ISPC: ISPCParser,
     Rust: RustParser,
-    Nim: NimParser,
+    Nim: NimParser
 };
diff --git a/lib/compilers/assembly.js b/lib/compilers/assembly.js
index 076b83c01..25156ae22 100644
--- a/lib/compilers/assembly.js
+++ b/lib/compilers/assembly.js
@@ -49,25 +49,24 @@ class AssemblyCompiler extends BaseCompiler {
         return [];
     }
 
-    runCompiler(compiler, options, inputFilename, execOptions) {
+    async runCompiler(compiler, options, inputFilename, execOptions) {
         if (!execOptions) {
             execOptions = this.getDefaultExecOptions();
         }
         execOptions.customCwd = path.dirname(inputFilename);
 
-        return this.exec(compiler, options, execOptions).then(result => {
-            result.inputFilename = inputFilename;
-            result.stdout = utils.parseOutput(result.stdout, inputFilename);
-            result.stderr = utils.parseOutput(result.stderr, inputFilename);
-            return result;
-        });
+        const result = await this.exec(compiler, options, execOptions);
+        result.inputFilename = inputFilename;
+        result.stdout = utils.parseOutput(result.stdout, inputFilename);
+        result.stderr = utils.parseOutput(result.stderr, inputFilename);
+        return result;
     }
 
     checkOutputFileAndDoPostProcess(asmResult, outputFilename, filters) {
         return this.postProcess(asmResult, outputFilename, filters);
     }
 
-    getGeneratedOutputfilename(inputFilename) {
+    getGeneratedOutputFilename(inputFilename) {
         const outputFolder = path.dirname(inputFilename);
 
         return new Promise((resolve, reject) => {
@@ -82,24 +81,20 @@ class AssemblyCompiler extends BaseCompiler {
         });
     }
 
-    objdump(outputFilename, result, maxSize, intelAsm, demangle) {
-        return this.getGeneratedOutputfilename(outputFilename)
-            .then((realOutputFilename) => {
-                const dirPath = path.dirname(realOutputFilename);
-                let args = ["-d", realOutputFilename, "-l", "--insn-width=16"];
-                if (demangle) args = args.concat("-C");
-                if (intelAsm) args = args.concat(["-M", "intel"]);
-                return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath})
-                    .then(objResult => {
-                        result.asm = objResult.stdout;
-                        if (objResult.code !== 0) {
-                            result.asm = "<No output: objdump returned " + objResult.code + ">";
-                        }
-                        return result;
-                    });
-            });
+    async objdump(outputFilename, result, maxSize, intelAsm, demangle) {
+        const realOutputFilename = await this.getGeneratedOutputFilename(outputFilename);
+        const dirPath = path.dirname(realOutputFilename);
+        let args = ["-d", realOutputFilename, "-l", "--insn-width=16"];
+        if (demangle) args = args.concat("-C");
+        if (intelAsm) args = args.concat(["-M", "intel"]);
+        const objResult = await this.exec(
+            this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath});
+        result.asm = objResult.stdout;
+        if (objResult.code !== 0) {
+            result.asm = "<No output: objdump returned " + objResult.code + ">";
+        }
+        return result;
     }
 }
 
-
 module.exports = AssemblyCompiler;
diff --git a/lib/compilers/clean.js b/lib/compilers/clean.js
index 5ed68c13a..e9f724954 100644
--- a/lib/compilers/clean.js
+++ b/lib/compilers/clean.js
@@ -72,7 +72,7 @@ class CleanCompiler extends BaseCompiler {
         }).join("\n");
     }
 
-    runCompiler(compiler, options, inputFilename, execOptions) {
+    async runCompiler(compiler, options, inputFilename, execOptions) {
         const tmpDir = path.dirname(inputFilename);
         const moduleName = path.basename(inputFilename, '.icl');
         const compilerPath = path.dirname(compiler);
@@ -83,32 +83,25 @@ class CleanCompiler extends BaseCompiler {
         options.pop();
         options.push(moduleName);
 
-        return this.exec(compiler, options, execOptions).then(result => {
-            return new Promise((resolve) => {
-                result.inputFilename = inputFilename;
-                result.stdout = utils.parseOutput(this.preprocessOutput(result.stdout), inputFilename);
-                result.stderr = utils.parseOutput(this.preprocessOutput(result.stderr), inputFilename);
-                if (!options.includes("-S")) {
-                    const aout = path.join(tmpDir, "a.out");
-                    fs.stat(aout).then(() => {
-                        fs.copyFile(aout, this.getOutputFilename(tmpDir)).then(() => {
-                            result.code = 0;
-                            resolve(result);
-                        });
-                    }).catch(() => {
-                        result.code = 1;
-                        resolve(result);
-                    });
-                } else {
-                    fs.stat(this.getOutputFilename(tmpDir)).then(() => {
-                        result.code = 0;
-                        resolve(result);
-                    }).catch(() => {
-                        resolve(result);
-                    });
-                }
-            });
-        });
+        const result = await this.exec(compiler, options, execOptions);
+        result.inputFilename = inputFilename;
+        result.stdout = utils.parseOutput(this.preprocessOutput(result.stdout), inputFilename);
+        result.stderr = utils.parseOutput(this.preprocessOutput(result.stderr), inputFilename);
+
+        if (!options.includes("-S")) {
+            const aOut = path.join(tmpDir, "a.out");
+            if (await fs.pathExists(aOut)) {
+                await fs.copyFile(aOut, this.getOutputFilename(tmpDir));
+                result.code = 0;
+            } else {
+                result.code = 1;
+            }
+        } else {
+            if (await fs.pathExists(this.getOutputFilename(tmpDir))) {
+                result.code = 0;
+            }
+        }
+        return result;
     }
 }
 
diff --git a/lib/compilers/dmd.js b/lib/compilers/dmd.js
index 20f50142d..89a8d753d 100644
--- a/lib/compilers/dmd.js
+++ b/lib/compilers/dmd.js
@@ -40,11 +40,12 @@ class DMDCompiler extends BaseCompiler {
         return ['-g', '-of' + this.filename(outputFilename)];
     }
 
-    execPostProcess(result, postProcesses, outputFilename, maxSize) {
+    async execPostProcess(result, postProcesses, outputFilename, maxSize) {
         const dirPath = path.dirname(outputFilename);
         const lPath = path.basename(outputFilename);
-        return this.exec(postProcesses[0], ["-l", lPath], {customCwd: dirPath, maxOutput: maxSize})
-            .then(postResult => this.handlePostProcessResult(result, postResult));
+        return this.handlePostProcessResult(
+            result,
+            await this.exec(postProcesses[0], ["-l", lPath], {customCwd: dirPath, maxOutput: maxSize}));
     }
 
     getOutputFilename(dirPath, outputFilebase) {
@@ -57,19 +58,18 @@ class DMDCompiler extends BaseCompiler {
         return this.runCompiler(compiler, options, inputFilename, execOptions);
     }
 
-    objdump(outputFilename, result, maxSize, intelAsm, demangle) {
+    async objdump(outputFilename, result, maxSize, intelAsm, demangle) {
         const dirPath = path.dirname(outputFilename);
         let args = ["-d", outputFilename, "-l", "--insn-width=16"];
         if (demangle) args = args.concat("-C");
         if (intelAsm) args = args.concat(["-M", "intel"]);
-        return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath})
-            .then(objResult => {
-                result.asm = objResult.stdout;
-                if (objResult.code !== 0) {
-                    result.asm = `<No output: objdump returned ${objResult.code}>`;
-                }
-                return result;
-            });
+        const objResult = await this.exec(
+            this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath});
+        result.asm = objResult.stdout;
+        if (objResult.code !== 0) {
+            result.asm = `<No output: objdump returned ${objResult.code}>`;
+        }
+        return result;
     }
 
     getArgumentParser() {
diff --git a/lib/compilers/fortran.js b/lib/compilers/fortran.js
index c0086c973..d38ee8ebc 100644
--- a/lib/compilers/fortran.js
+++ b/lib/compilers/fortran.js
@@ -28,7 +28,7 @@ const BaseCompiler = require('../base-compiler'),
     utils = require('../utils');
 
 class FortranCompiler extends BaseCompiler {
-    runCompiler(compiler, options, inputFilename, execOptions) {
+    async runCompiler(compiler, options, inputFilename, execOptions) {
         if (!execOptions) {
             execOptions = this.getDefaultExecOptions();
         }
@@ -36,12 +36,11 @@ class FortranCompiler extends BaseCompiler {
         // This makes it possible to generate .mod files.
         execOptions.customCwd = path.dirname(inputFilename);
 
-        return this.exec(compiler, options, execOptions).then(result => {
-            result.inputFilename = inputFilename;
-            result.stdout = utils.parseOutput(result.stdout, inputFilename);
-            result.stderr = utils.parseOutput(result.stderr, inputFilename);
-            return result;
-        });
+        const result = await this.exec(compiler, options, execOptions);
+        result.inputFilename = inputFilename;
+        result.stdout = utils.parseOutput(result.stdout, inputFilename);
+        result.stderr = utils.parseOutput(result.stderr, inputFilename);
+        return result;
     }
 }
 
diff --git a/lib/compilers/java.js b/lib/compilers/java.js
index 132993ed6..36b397f59 100644
--- a/lib/compilers/java.js
+++ b/lib/compilers/java.js
@@ -39,50 +39,40 @@ class JavaCompiler extends BaseCompiler {
         super(compilerInfo, env);
     }
 
-
-    objdump(outputFilename, result, maxSize) {
+    async objdump(outputFilename, result, maxSize) {
         const dirPath = path.dirname(outputFilename);
-        return fs.readdir(dirPath)
-            .then(files => {
-                logger.verbose("Class files: ", files);
-                return files.filter(f => f.endsWith('.class'));
-            })
-            .then(classFiles => {
-                return Promise.all(classFiles.map(classFile => {
-                    const args = [
-                        // Prints out disassembled code, i.e., the instructions that comprise the Java bytecodes,
-                        // for each of the methods in the class.
-                        '-c',
-                        // Prints out line and local variable tables.
-                        '-l',
-                        classFile
-                    ];
-                    return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath})
-                        .then(objResult => {
-                            const oneResult = {};
-                            oneResult.asm = objResult.stdout;
-                            if (objResult.code !== 0) {
-                                oneResult.asm = "<No output: javap returned " + objResult.code + ">";
-                            }
-                            return oneResult;
-                        });
-                }));
-            })
-            .then(results => {
-                const merged = {asm: []};
-                for (const result of results) {
-                    const asmBackup = merged.asm;
-                    Object.assign(merged, result);
-                    merged.asm = asmBackup;
-                    merged.asm.push(result.asm);
-                }
+        const files = await fs.readdir(dirPath);
+        logger.verbose("Class files: ", files);
+        const results = await Promise.all(files.filter(f => f.endsWith('.class')).map(async classFile => {
+            const args = [
+                // Prints out disassembled code, i.e., the instructions that comprise the Java bytecodes,
+                // for each of the methods in the class.
+                '-c',
+                // Prints out line and local variable tables.
+                '-l',
+                classFile
+            ];
+            const objResult = await this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath});
+            const oneResult = {};
+            oneResult.asm = objResult.stdout;
+            if (objResult.code !== 0) {
+                oneResult.asm = "<No output: javap returned " + objResult.code + ">";
+            }
+            return oneResult;
+        }));
+
+        const merged = {asm: []};
+        for (const result of results) {
+            const asmBackup = merged.asm;
+            Object.assign(merged, result);
+            merged.asm = asmBackup;
+            merged.asm.push(result.asm);
+        }
 
-                result.asm = merged.asm;
-                return result;
-            });
+        result.asm = merged.asm;
+        return result;
     }
 
-
     optionsForFilter(filters) {
         // Forcibly enable javap
         filters.binary = true;
@@ -101,7 +91,6 @@ class JavaCompiler extends BaseCompiler {
         return path.join(dirPath, `${path.basename(this.compileFilename, this.lang.extensions[0])}.class`);
     }
 
-
     filterUserOptions(userOptions) {
         const filteredOptions = [];
         let toSkip = 0;
diff --git a/lib/compilers/nim.js b/lib/compilers/nim.js
index b9de53645..de920d9d7 100644
--- a/lib/compilers/nim.js
+++ b/lib/compilers/nim.js
@@ -91,22 +91,21 @@ class NimCompiler extends BaseCompiler {
         return path.join(cacheDir, resultName);
     }
 
-    postProcess(result, outputFilename, filters) {
-        let options = result.compilationOptions;
-        let setup = Promise.resolve("");
+    async postProcess(result, outputFilename, filters) {
+        const options = result.compilationOptions;
         const cacheDir = this.cacheDir(outputFilename);
-        const cleanup = () => fs.remove(cacheDir);
-
-        if (_.intersection(options, ["js", "check"]).length)
-            filters.binary = false;
-        else {
-            filters.binary = true;
-            const objFile = this.getCacheFile(options, result.inputFilename, cacheDir);
-            setup = fs.move(objFile, outputFilename);
+        try {
+            if (_.intersection(options, ["js", "check"]).length)
+                filters.binary = false;
+            else {
+                filters.binary = true;
+                const objFile = this.getCacheFile(options, result.inputFilename, cacheDir);
+                await fs.move(objFile, outputFilename);
+            }
+            return super.postProcess(result, outputFilename, filters);
+        } finally {
+            await fs.remove(cacheDir);
         }
-
-        const postProcess = () => super.postProcess(result, outputFilename, filters);
-        return setup.then(postProcess).finally(cleanup);
     }
 
     getSharedLibraryPathsAsArguments(/*libraries*/) {
diff --git a/lib/compilers/nvcc.js b/lib/compilers/nvcc.js
index 8bbedc4c9..7aed5a4c6 100644
--- a/lib/compilers/nvcc.js
+++ b/lib/compilers/nvcc.js
@@ -49,19 +49,17 @@ class NvccCompiler extends BaseCompiler {
         return argumentParsers.Clang;
     }
 
-    objdump(outputFilename, result, maxSize) {
+    async objdump(outputFilename, result, maxSize) {
         // For nvdisasm.
-        let args = [outputFilename, "-c", "-g", "-hex"];
+        const args = [outputFilename, "-c", "-g", "-hex"];
         const execOptions = {maxOutput: maxSize, customCwd: path.dirname(outputFilename)};
 
-        return this.exec(this.compiler.objdumper, args, execOptions)
-            .then(objResult => {
-                result.asm = objResult.stdout;
-                if (objResult.code !== 0) {
-                    result.asm = `<No output: nvdisasm returned ${objResult.code}>`;
-                }
-                return result;
-            });
+        const objResult = await this.exec(this.compiler.objdumper, args, execOptions);
+        result.asm = objResult.stdout;
+        if (objResult.code !== 0) {
+            result.asm = `<No output: nvdisasm returned ${objResult.code}>`;
+        }
+        return result;
     }
 }
 
diff --git a/lib/compilers/pascal.js b/lib/compilers/pascal.js
index 4e06c7cd8..feca7a79b 100644
--- a/lib/compilers/pascal.js
+++ b/lib/compilers/pascal.js
@@ -26,7 +26,7 @@
 const BaseCompiler = require('../base-compiler'),
     utils = require('../utils'),
     _ = require('underscore'),
-    fs = require("fs"),
+    fs = require("fs-extra"),
     path = require("path"),
     argumentParsers = require("./argument-parsers");
 
@@ -95,35 +95,33 @@ class FPCCompiler extends BaseCompiler {
         return input;
     }
 
-    objdump(outputFilename, result, maxSize, intelAsm, demangle) {
+    async objdump(outputFilename, result, maxSize, intelAsm, demangle) {
         const dirPath = path.dirname(outputFilename);
         outputFilename = this.getExecutableFilename(dirPath);
         let args = ["-d", outputFilename, "-l", "--insn-width=16"];
         if (demangle) args = args.concat(["-C"]);
         if (intelAsm) args = args.concat(["-M", "intel"]);
-        return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath})
-            .then(objResult => {
-                if (objResult.code !== 0) {
-                    result.asm = "<No output: objdump returned " + objResult.code + ">";
-                } else {
-                    result.asm = FPCCompiler.preProcessBinaryAsm(objResult.stdout);
-                }
-                return result;
-            });
+        const objResult = await this.exec(
+            this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath});
+        if (objResult.code !== 0) {
+            result.asm = "<No output: objdump returned " + objResult.code + ">";
+        } else {
+            result.asm = FPCCompiler.preProcessBinaryAsm(objResult.stdout);
+        }
+        return result;
     }
 
-    saveDummyProjectFile(filename) {
+    async saveDummyProjectFile(filename) {
         const unitName = path.basename(this.compileFilename, this.lang.extensions[0]);
 
-        fs.writeFileSync(filename,
+        await fs.writeFile(filename,
             "program prog; " +
             "uses " + unitName + " in '" + this.compileFilename + "'; " +
             "begin " +
-            "end.", () => {
-            });
+            "end.");
     }
 
-    runCompiler(compiler, options, inputFilename, execOptions) {
+    async runCompiler(compiler, options, inputFilename, execOptions) {
         if (!execOptions) {
             execOptions = this.getDefaultExecOptions();
         }
@@ -132,17 +130,14 @@ class FPCCompiler extends BaseCompiler {
         const projectFile = path.join(dirPath, "prog.dpr");
         execOptions.customCwd = dirPath;
 
-        this.saveDummyProjectFile(projectFile);
+        await this.saveDummyProjectFile(projectFile);
 
         options.pop();
         options.push('-FE' + dirPath);
         options.push('-B');
         options.push(projectFile);
 
-        return this.exec(compiler, options, execOptions)
-            .then(result => {
-                return this.parseOutput(result, inputFilename, dirPath);
-            });
+        return this.parseOutput(await this.exec(compiler, options, execOptions), inputFilename, dirPath);
     }
 
     parseOutput(result, inputFilename, tempPath) {
diff --git a/lib/compilers/win32.js b/lib/compilers/win32.js
index 840e3bd2c..5b9616cec 100644
--- a/lib/compilers/win32.js
+++ b/lib/compilers/win32.js
@@ -46,22 +46,20 @@ class Win32Compiler extends BaseCompiler {
         return this.getOutputFilename(dirPath, outputFilebase) + ".exe";
     }
 
-    objdump(outputFilename, result, maxSize, intelAsm) {
+    async objdump(outputFilename, result, maxSize, intelAsm) {
         const dirPath = path.dirname(outputFilename);
         outputFilename = this.getExecutableFilename(path.dirname(outputFilename), "output");
 
         let args = ["-d", outputFilename];
         if (intelAsm) args = args.concat(["-M", "intel"]);
-        return this.exec(this.compiler.objdumper, args, {maxOutput: 0, customCwd: dirPath})
-            .then((objResult) => {
-                if (objResult.code !== 0) {
-                    result.asm = "<No output: objdump returned " + objResult.code + ">";
-                } else {
-                    result.asm = objResult.stdout;
-                }
+        const objResult = await this.exec(this.compiler.objdumper, args, {maxOutput: 0, customCwd: dirPath});
+        if (objResult.code !== 0) {
+            result.asm = "<No output: objdump returned " + objResult.code + ">";
+        } else {
+            result.asm = objResult.stdout;
+        }
 
-                return result;
-            });
+        return result;
     }
 
     getSharedLibraryPathsAsArguments(libraries) {
@@ -146,7 +144,7 @@ class Win32Compiler extends BaseCompiler {
                 '/FA',
                 '/c',
                 '/Fa' + this.filename(outputFilename),
-                '/Fo' + this.filename(outputFilename + '.obj'),
+                '/Fo' + this.filename(outputFilename + '.obj')
             ];
         }
     }
diff --git a/lib/compilers/wine-vc.js b/lib/compilers/wine-vc.js
index 0e7f8b50e..c8a50bf03 100644
--- a/lib/compilers/wine-vc.js
+++ b/lib/compilers/wine-vc.js
@@ -57,22 +57,20 @@ class WineVcCompiler extends BaseCompiler {
         return this.getOutputFilename(dirPath, outputFilebase) + ".exe";
     }
 
-    objdump(outputFilename, result, maxSize, intelAsm) {
+    async objdump(outputFilename, result, maxSize, intelAsm) {
         const dirPath = path.dirname(outputFilename);
         outputFilename = this.getExecutableFilename(dirPath, "output");
 
         let args = ["-d", outputFilename];
         if (intelAsm) args = args.concat(["-M", "intel"]);
-        return this.exec(this.compiler.objdumper, args, {maxOutput: 0, customCwd: dirPath})
-            .then((objResult) => {
-                if (objResult.code !== 0) {
-                    result.asm = "<No output: objdump returned " + objResult.code + ">";
-                } else {
-                    result.asm = objResult.stdout;
-                }
-
-                return result;
-            });
+        const objResult = await this.exec(this.compiler.objdumper, args, {maxOutput: 0, customCwd: dirPath});
+        if (objResult.code !== 0) {
+            result.asm = "<No output: objdump returned " + objResult.code + ">";
+        } else {
+            result.asm = objResult.stdout;
+        }
+
+        return result;
     }
 
     optionsForFilter(filters, outputFilename) {
diff --git a/lib/demangler-win32.js b/lib/demangler-win32.js
index f4c5662f3..2fd6dc95f 100644
--- a/lib/demangler-win32.js
+++ b/lib/demangler-win32.js
@@ -86,33 +86,31 @@ class DemanglerWin32 extends DemanglerCPP {
         return this.result;
     }
 
-    execDemangler() {
-        let translations = {};
+    async execDemangler() {
+        const translations = {};
 
-        const demangleSingleSet = names => {
+        const demangleSingleSet = async names => {
             const args = [this.flags, ...names];
-            return this.compiler.exec(this.demanglerExe, args, this.compiler.getDefaultExecOptions())
-                .then(output => {
-                    const outputArray = utils.splitLines(output.stdout);
-
-                    for (let i = 0; i < outputArray.length; ++i) {
-                        let tmp = outputArray[i].match(/^Undecoration of :- "(.*)"/);
-                        if (tmp) {
-                            const decoratedName = tmp[1];
-                            ++i;
-                            tmp = outputArray[i].match(/^is :- "(.*)"/);
-                            if (!tmp) {
-                                logger.error(`Broken undname: ${outputArray[i - 1]}, ${outputArray[i]}`);
-                            } else {
-                                if (this.hasQuotesAroundDecoratedLabels) {
-                                    translations[`"${decoratedName}"`] = tmp[1];
-                                } else {
-                                    translations[decoratedName] = tmp[1];
-                                }
-                            }
+            const output = await this.compiler.exec(this.demanglerExe, args, this.compiler.getDefaultExecOptions());
+            const outputArray = utils.splitLines(output.stdout);
+
+            for (let i = 0; i < outputArray.length; ++i) {
+                let tmp = outputArray[i].match(/^Undecoration of :- "(.*)"/);
+                if (tmp) {
+                    const decoratedName = tmp[1];
+                    ++i;
+                    tmp = outputArray[i].match(/^is :- "(.*)"/);
+                    if (!tmp) {
+                        logger.error(`Broken undname: ${outputArray[i - 1]}, ${outputArray[i]}`);
+                    } else {
+                        if (this.hasQuotesAroundDecoratedLabels) {
+                            translations[`"${decoratedName}"`] = tmp[1];
+                        } else {
+                            translations[decoratedName] = tmp[1];
                         }
                     }
-                });
+                }
+            }
         };
 
         // give some space for `undname` as well as the flag
@@ -143,8 +141,8 @@ class DemanglerWin32 extends DemanglerCPP {
             commandLineArray.push(currentArray);
         }
 
-        const demangleAllArgs = commandLineArray.map(demangleSingleSet);
-        return Promise.all(demangleAllArgs).then(() => translations);
+        await Promise.all(commandLineArray.map(demangleSingleSet));
+        return translations;
     }
 
     async process(result) {
diff --git a/lib/handlers/compile.js b/lib/handlers/compile.js
index d42c21668..73cd2dc97 100644
--- a/lib/handlers/compile.js
+++ b/lib/handlers/compile.js
@@ -121,8 +121,7 @@ class CompileHandler {
                     return cached;
                 }
                 const compilerObj = new this.factories[type](compiler, this.compilerEnv);
-                compilerObj.mtime = res.mtime;
-                return compilerObj.initialise();
+                return compilerObj.initialise(res.mtime);
             } catch (err) {
                 logger.warn(`Unable to stat ${compiler.id} compiler binary: `, err);
                 return null;
@@ -132,23 +131,26 @@ class CompileHandler {
         }
     }
 
-    setCompilers(compilers) {
+    async setCompilers(compilers) {
         // Be careful not to update this.compilersById until we can replace it entirely.
         const compilersById = {};
-        return Promise.all(_.map(compilers, this.create, this))
-            .then(_.compact)
-            .then(compilers => {
-                _.each(compilers, compiler => {
-                    const langId = compiler.compiler.lang;
-                    if (!compilersById[langId]) compilersById[langId] = {};
-                    compilersById[langId][compiler.compiler.id] = compiler;
-
-                    if (this.awsProps) compiler.possibleArguments.loadFromStorage(this.awsProps);
-                });
-                this.compilersById = compilersById;
-                return _.map(compilers, compiler => compiler.getInfo());
-            })
-            .catch(err => logger.error(err));
+        try {
+            const createdCompilers = _.compact(await Promise.all(_.map(compilers, this.create, this)));
+            for (const compiler of createdCompilers) {
+                const langId = compiler.compiler.lang;
+                if (!compilersById[langId]) compilersById[langId] = {};
+                compilersById[langId][compiler.compiler.id] = compiler;
+            }
+            if (this.awsProps) {
+                logger.info("Fetching possible arguments from storage");
+                await Promise.all(createdCompilers.map(
+                    compiler => compiler.possibleArguments.loadFromStorage(this.awsProps)));
+            }
+            this.compilersById = compilersById;
+            return createdCompilers.map(compiler => compiler.getInfo());
+        } catch (err) {
+            logger.error("Exception while processing compilers:", err);
+        }
     }
 
     findCompiler(langId, compilerId) {
diff --git a/lib/handlers/formatting.js b/lib/handlers/formatting.js
index 53ce5117a..5ebdd7a2e 100644
--- a/lib/handlers/formatting.js
+++ b/lib/handlers/formatting.js
@@ -35,7 +35,7 @@ class Formatter {
         _.each(formatters, this.fetchToolInfo.bind(this));
     }
 
-    fetchToolInfo(formatter) {
+    async fetchToolInfo(formatter) {
         const exe = this.ceProps(`formatter.${formatter}.exe`);
         if (!exe) {
             logger.warn(`Formatter ${formatter} does not have a valid executable. Skipping...`);
@@ -43,17 +43,18 @@ class Formatter {
         }
         const versionArg = this.ceProps(`formatter.${formatter}.version`, '--version');
         const versionRe = this.ceProps(`formatter.${formatter}.versionRe`, '.*');
-        this.run(exe, [versionArg], {})
-            .then(result => {
-                const match = result.stdout.match(versionRe);
-                this.tools[formatter] = {
-                    exe: exe,
-                    version: match ? match[0] : result.stdout,
-                    name: this.ceProps(`formatter.${formatter}.name`, exe),
-                    styles: this.ceProps(`formatter.${formatter}.styles`, "").split(':')
-                };
-            })
-            .catch(logger.warn); // Log things like ENOENT errors as warnings
+        try {
+            const result = await this.run(exe, [versionArg], {});
+            const match = result.stdout.match(versionRe);
+            this.tools[formatter] = {
+                exe: exe,
+                version: match ? match[0] : result.stdout,
+                name: this.ceProps(`formatter.${formatter}.name`, exe),
+                styles: this.ceProps(`formatter.${formatter}.styles`, "").split(':')
+            };
+        } catch (err) {
+            logger.warn("Error while fetching tool info for ${exe}:", {err});
+        }
     }
 
     supportsStyle(tool, style) {
@@ -91,31 +92,33 @@ class Formatter {
         return true;
     }
 
-    formatHandler(req, res) {
+    async formatHandler(req, res) {
         if (!this.validateFormatRequest(req, res)) return;
-        this.formatCode(this.tools[req.params.tool], [`-style=${req.body.base}`], {input: req.body.source})
-            .then(result => {
-                res.send({
-                    exit: result.code,
-                    answer: result.stdout || ""
-                });
-            })
-            .catch(ex => {
-                // Unexpected problem when running the formatter
-                res.status(500);
-                res.send({
-                    exit: 1,
-                    thrown: true,
-                    answer: ex.message || "Internal server error"
-                });
+        try {
+            const result = await this.formatCode(
+                this.tools[req.params.tool],
+                [`-style=${req.body.base}`],
+                {input: req.body.source});
+            res.send({
+                exit: result.code,
+                answer: result.stdout || ""
             });
+        } catch (ex) {
+            // Unexpected problem when running the formatter
+            res.status(500);
+            res.send({
+                exit: 1,
+                thrown: true,
+                answer: ex.message || "Internal server error"
+            });
+        }
     }
 
-    run(exe, args, options) {
+    async run(exe, args, options) {
         return exec.execute(exe, args, options);
     }
 
-    formatCode(tool, args, options) {
+    async formatCode(tool, args, options) {
         return this.run(tool.exe, args, options);
     }
 
diff --git a/lib/s3-handler.js b/lib/s3-handler.js
index b17a0fec8..d4aa39ae8 100644
--- a/lib/s3-handler.js
+++ b/lib/s3-handler.js
@@ -25,6 +25,8 @@
 
 const AWS = require('aws-sdk');
 
+const NoSuchKey = 'NoSuchKey';
+
 class S3Bucket {
     constructor(bucket, region) {
         this.instance = new AWS.S3({region});
@@ -32,37 +34,32 @@ class S3Bucket {
         this.region = region;
     }
 
-    get(key, path) {
-        return this.instance.getObject({Bucket: this.bucket, Key: `${path}/${key}`})
-            .promise()
-            .then(result => {
-                return {hit: true, data: result.Body};
-            })
-            .catch(x => {
-                if (x.code === 'NoSuchKey') return {hit: false};
-                throw x;
-            });
+    async get(key, path) {
+        try {
+            const result = await this.instance.getObject({Bucket: this.bucket, Key: `${path}/${key}`}).promise();
+            return {hit: true, data: result.Body};
+        } catch (x) {
+            if (x.code === NoSuchKey) return {hit: false};
+            throw x;
+        }
     }
 
-    delete(key, path) {
-        return this.instance.deleteObject({Bucket: this.bucket, Key: `${path}/${key}`})
-            .promise()
-            .then(() => {
-                return true;
-            })
-            .catch(x => {
-                if (x.code === 'NoSuchKey') return false;
-                throw x;
-            });
+    async delete(key, path) {
+        try {
+            await this.instance.deleteObject({Bucket: this.bucket, Key: `${path}/${key}`}).promise();
+        } catch (x) {
+            if (x.code === NoSuchKey) return false;
+            throw x;
+        }
     }
 
-    put(key, value, path, options) {
-        const metadata = options.metadata || {};
-        const redundancy = options.redundancy || "STANDARD";
+    async put(key, value, path, options) {
         return this.instance.putObject({
-            Bucket: this.bucket, Key: `${path}/${key}`, Body: value,
-            StorageClass: redundancy,
-            Metadata: metadata
+            Bucket: this.bucket,
+            Key: `${path}/${key}`,
+            Body: value,
+            StorageClass: options.redundancy || "STANDARD",
+            Metadata: options.metadata || {}
         }).promise();
     }
 }
diff --git a/lib/storage/storage-local.js b/lib/storage/storage-local.js
index e967dc108..e88278739 100644
--- a/lib/storage/storage-local.js
+++ b/lib/storage/storage-local.js
@@ -40,77 +40,81 @@ class StorageLocal extends StorageBase {
         logger.info(`Using local storage solution on ${this.storageFolder}`);
     }
 
-    storeItem(item) {
+    async storeItem(item) {
         const filePath = path.join(this.storageFolder, item.uniqueSubHash);
-        return fs.writeJson(filePath, item, {encoding: 'utf8'})
-            .then(() => item)
-            .catch(logger.error);
+        try {
+            await fs.writeJson(filePath, item, {encoding: 'utf8'});
+        } catch (err) {
+            logger.error(`Caught exception while trying to store to ${path}`, err);
+            throw err;
+        }
+        return item;
     }
 
-    findUniqueSubhash(hash) {
+    async findUniqueSubhash(hash) {
         logger.info(`Finding local unique subhash for ${hash}`);
         // This currently works on a hardcoded, local directory.
-        return fs.readdir(this.storageFolder)
-            .then(files => {
-                let prefix = hash.substring(0, MIN_STORED_ID_LENGTH);
-                const filenames = _.chain(files)
-                    .filter(filename => filename.startsWith(prefix))
-                    .sort()
-                    .value();
-                for (let i = MIN_STORED_ID_LENGTH; i < hash.length - 1; i++) {
-                    let subHash = hash.substring(0, i);
-                    // Check if the current subHash is present in the array
-                    const index = _.indexOf(filenames, subHash, true);
-                    if (index === -1) {
-                        // Current base is not present, we have a new config in our hands
+        try {
+            const files = await fs.readdir(this.storageFolder);
+            let prefix = hash.substring(0, MIN_STORED_ID_LENGTH);
+            const filenames = _.chain(files)
+                .filter(filename => filename.startsWith(prefix))
+                .sort()
+                .value();
+            for (let i = MIN_STORED_ID_LENGTH; i < hash.length - 1; i++) {
+                let subHash = hash.substring(0, i);
+                // Check if the current subHash is present in the array
+                const index = _.indexOf(filenames, subHash, true);
+                if (index === -1) {
+                    // Current base is not present, we have a new config in our hands
+                    return {
+                        prefix: prefix,
+                        uniqueSubHash: subHash,
+                        alreadyPresent: false
+                    };
+                } else {
+                    const expectedPath = path.join(this.storageFolder, subHash);
+                    const item = await fs.readJson(expectedPath);
+                    /* If the hashes coincide, it means this config has already been stored.
+                     * Else, keep looking
+                     */
+                    if (item.fullHash === hash) {
                         return {
                             prefix: prefix,
                             uniqueSubHash: subHash,
-                            alreadyPresent: false
+                            alreadyPresent: true
                         };
-                    } else {
-                        const expectedPath = path.join(this.storageFolder, subHash);
-                        const item = fs.readJsonSync(expectedPath);
-                        /* If the hashes coincide, it means this config has already been stored.
-                         * Else, keep looking
-                         */
-                        if (item.fullHash === hash) {
-                            return {
-                                prefix: prefix,
-                                uniqueSubHash: subHash,
-                                alreadyPresent: true
-                            };
-                        }
                     }
                 }
-                // Causes why we're here: MIN_STORED_ID_LENGTH < hash.length. Something else?
-                throw 'Hash too small';
-            })
-            .catch(err => {
-                // IO error/Logic error, we have no way to store this right now. Please try again? What to do here?
-                logger.error(`Error when looking for unique local subhash for ${hash}: ${err}`);
-                return err;
-            });
+            }
+        } catch (err) {
+            // IO error/Logic error, we have no way to store this right now. Please try again? What to do here?
+            logger.error(`Error when looking for unique local subhash for ${hash}`, err);
+            throw err;
+        }
+        // Causes why we're here: MIN_STORED_ID_LENGTH < hash.length. Something else?
+        logger.error(`Hash too small when looking for unique local subhash for ${hash}`);
+        throw new Error('Hash too small');
     }
 
-    expandId(id) {
+    async expandId(id) {
         const expectedPath = path.join(this.storageFolder, id);
         logger.info(`Expanding local id ${id} to ${expectedPath}`);
-        return fs.readJson(expectedPath)
-            .then(item => {
-                return {
-                    config: item.config,
-                    specialMetadata: null
-                };
-            })
-            .catch(err => {
-                logger.error(err);
-                throw err;
-            });
+        try {
+            const item = await fs.readJson(expectedPath);
+            return {
+                config: item.config,
+                specialMetadata: null
+            };
+        } catch (err) {
+            // IO error/Logic error, we have no way to store this right now. Please try again? What to do here?
+            logger.error(`Error when expanding id ${id}`, err);
+            throw err;
+        }
     }
-    incrementViewCount() {
+
+    async incrementViewCount() {
         // Nothing to do here, we don't store stats for local storage
-        return Promise.resolve();
     }
 }
 
diff --git a/lib/storage/storage-null.js b/lib/storage/storage-null.js
index 064766cde..24a5f96f0 100644
--- a/lib/storage/storage-null.js
+++ b/lib/storage/storage-null.js
@@ -30,27 +30,27 @@ class StorageNull extends StorageBase {
         super(httpRootDir, compilerProps);
     }
 
-    storeItem(item) {
-        return Promise.resolve(item);
+    async storeItem(item) {
+        return item;
     }
 
-    findUniqueSubhash() {
-        return Promise.resolve({
+    async findUniqueSubhash() {
+        return {
             prefix: "null",
             uniqueSubHash: "null",
             alreadyPresent: true
-        });
+        };
     }
 
-    expandId() {
-        return Promise.resolve({
+    async expandId() {
+        return {
             config: "{}",
             specialMetadata: null
-        });
+        };
     }
 
-    incrementViewCount() {
-        return Promise.resolve();
+    async incrementViewCount() {
     }
 }
+
 module.exports = StorageNull;
diff --git a/lib/storage/storage-remote.js b/lib/storage/storage-remote.js
index 15b21a5c5..25d399547 100644
--- a/lib/storage/storage-remote.js
+++ b/lib/storage/storage-remote.js
@@ -80,8 +80,7 @@ class StorageRemote extends StorageBase {
         };
     }
 
-    incrementViewCount() {
-        return Promise.resolve();
+    async incrementViewCount() {
     }
 }
 
diff --git a/lib/storage/storage-s3.js b/lib/storage/storage-s3.js
index 1a14c0239..a8dd41424 100644
--- a/lib/storage/storage-s3.js
+++ b/lib/storage/storage-s3.js
@@ -47,7 +47,7 @@ class StorageS3 extends StorageBase {
         this.dynamoDb = new AWS.DynamoDB();
     }
 
-    storeItem(item, req) {
+    async storeItem(item, req) {
         logger.info(`Storing item ${item.prefix}`);
         const now = new Date();
         let ip = req.get('X-Forwarded-For') || anonymizeIp(req.ip);
@@ -57,84 +57,72 @@ class StorageS3 extends StorageBase {
             ip = `${anonymizeIp(ip.substring(0, commaIndex))}${ip.substring(commaIndex, ip.length)}`;
         }
         now.setSeconds(0, 0);
-        return Promise.all([
-            this.dynamoDb.putItem({
-                TableName: this.table,
-                Item: {
-                    prefix: {
-                        S: item.prefix
-                    },
-                    unique_subhash: {
-                        S: item.uniqueSubHash
-                    },
-                    full_hash: {
-                        S: item.fullHash
-                    },
-                    stats: {
-                        M: {
-                            clicks: {N: '0'}
-                        }
-                    },
-                    creation_ip: {
-                        S: ip
-                    },
-                    creation_date: {
-                        S: now.toISOString()
+        try {
+            await Promise.all([
+                this.dynamoDb.putItem({
+                    TableName: this.table,
+                    Item: {
+                        prefix: {S: item.prefix},
+                        unique_subhash: {S: item.uniqueSubHash},
+                        full_hash: {S: item.fullHash},
+                        stats: {
+                            M: {clicks: {N: '0'}}
+                        },
+                        creation_ip: {S: ip},
+                        creation_date: {S: now.toISOString()}
                     }
-                }
-            }).promise(),
-            this.s3.put(item.fullHash, item.config, this.prefix, {})
-        ])
-            .then(() => item)
-            .catch(logger.error);
+                }).promise(),
+                this.s3.put(item.fullHash, item.config, this.prefix, {})
+            ]);
+            return item;
+        } catch (err) {
+            logger.error("Unable to store item", {item, err});
+            throw err;
+        }
     }
 
-    findUniqueSubhash(hash) {
+    async findUniqueSubhash(hash) {
         const prefix = hash.substring(0, MIN_STORED_ID_LENGTH);
-        return this.dynamoDb.query({
+        const data = await this.dynamoDb.query({
             TableName: this.table,
             ProjectionExpression: 'unique_subhash, full_hash',
             KeyConditionExpression: 'prefix = :prefix',
-            ExpressionAttributeValues: {
-                ':prefix': {S: prefix}
-            }
-        }).promise()
-            .then(data => {
-                const subHashes = _.chain(data.Items)
-                    .pluck('unique_subhash')
-                    .pluck('S')
-                    .value();
-                const fullHashes = _.chain(data.Items)
-                    .pluck('full_hash')
-                    .pluck('S')
-                    .value();
-                for (let i = MIN_STORED_ID_LENGTH; i < hash.length - 1; i++) {
-                    let subHash = hash.substring(0, i);
-                    // Check if the current base is present in the subHashes array
-                    const index = _.indexOf(subHashes, subHash, true);
-                    if (index === -1) {
-                        // Current base is not present, we have a new config in our hands
-                        return {
-                            prefix: prefix,
-                            uniqueSubHash: subHash,
-                            alreadyPresent: false
-                        };
-                    } else {
-                        const itemHash = fullHashes[index];
-                        /* If the hashes coincide, it means this config has already been stored.
-                         * Else, keep looking
-                         */
-                        if (itemHash === hash) {
-                            return {
-                                prefix: prefix,
-                                uniqueSubHash: subHash,
-                                alreadyPresent: true
-                            };
-                        }
-                    }
+            ExpressionAttributeValues: {':prefix': {S: prefix}}
+        }).promise();
+        const subHashes = _.chain(data.Items)
+            .pluck('unique_subhash')
+            .pluck('S')
+            .value();
+        const fullHashes = _.chain(data.Items)
+            .pluck('full_hash')
+            .pluck('S')
+            .value();
+        for (let i = MIN_STORED_ID_LENGTH; i < hash.length - 1; i++) {
+            let subHash = hash.substring(0, i);
+            // Check if the current base is present in the subHashes array
+            const index = _.indexOf(subHashes, subHash, true);
+            if (index === -1) {
+                // Current base is not present, we have a new config in our hands
+                return {
+                    prefix: prefix,
+                    uniqueSubHash: subHash,
+                    alreadyPresent: false
+                };
+            } else {
+                const itemHash = fullHashes[index];
+                /* If the hashes coincide, it means this config has already been stored.
+                 * Else, keep looking
+                 */
+                if (itemHash === hash) {
+                    return {
+                        prefix: prefix,
+                        uniqueSubHash: subHash,
+                        alreadyPresent: true
+                    };
                 }
-                throw new Error(`Could not find unique subhash for hash "${hash}"`);
-            });
+            }
+        }
+        throw new Error(`Could not find unique subhash for hash "${hash}"`);
     }
 
     getKeyStruct(id) {
@@ -144,52 +132,43 @@ class StorageS3 extends StorageBase {
         };
     }
 
-    expandId(id) {
+    async expandId(id) {
         // By just getting the item and not trying to update it, we save an update when the link does not exist
         // for which we have less resources allocated, but get one extra read (But we do have more reserved for it)
-        return this.dynamoDb.getItem({
+        const item = await this.dynamoDb.getItem({
             TableName: this.table,
             Key: this.getKeyStruct(id)
-        })
-            .promise()
-            .then(item => {
-                const attributes = item.Item;
-                if (attributes) {
-                    return this.s3.get(attributes.full_hash.S, this.prefix)
-                        .then(result => {
-                            // If we're here, we are pretty confident there is a match. But never hurts to double check
-                            if (result.hit) {
-                                const metadata = attributes.named_metadata ? attributes.named_metadata.M : null;
-                                return {
-                                    config: result.data.toString(),
-                                    specialMetadata: metadata
-                                };
-                            } else {
-                                throw new Error(`ID ${id} not present in storage`);
-                            }
-                        })
-                        .catch(err => {
-                            logger.error(err.message);
-                            throw err;
-                        });
-                } else {
-                    return Promise.reject();
-                }
-            }); // Exceptions caught at caller
+        }).promise();
+
+        const attributes = item.Item;
+        if (!attributes)
+            throw new Error(`Missing attributes on ${id}`);
+        const result = await this.s3.get(attributes.full_hash.S, this.prefix);
+        // If we're here, we are pretty confident there is a match. But never hurts to double check
+        if (!result.hit)
+            throw new Error(`ID ${id} not present in storage`);
+        const metadata = attributes.named_metadata ? attributes.named_metadata.M : null;
+        return {
+            config: result.data.toString(),
+            specialMetadata: metadata
+        };
     }
 
-    incrementViewCount(id) {
-        return this.dynamoDb.updateItem({
-            TableName: this.table,
-            Key: this.getKeyStruct(id),
-            UpdateExpression: 'SET stats.clicks = stats.clicks + :inc',
-            ExpressionAttributeValues: {
-                ':inc': {N: '1'}
-            },
-            ReturnValues: 'NONE'
-        }).promise()
-        // Swallow up errors
-            .catch(err => logger.error(`Error when incrementing view count for ${id} - ${err.message}`));
+    async incrementViewCount(id) {
+        try {
+            await this.dynamoDb.updateItem({
+                TableName: this.table,
+                Key: this.getKeyStruct(id),
+                UpdateExpression: 'SET stats.clicks = stats.clicks + :inc',
+                ExpressionAttributeValues: {
+                    ':inc': {N: '1'}
+                },
+                ReturnValues: 'NONE'
+            }).promise();
+        } catch (err) {
+            // Swallow up errors
+            logger.error(`Error when incrementing view count for ${id}`, err);
+        }
     }
 }
 
diff --git a/lib/storage/storage.js b/lib/storage/storage.js
index 8e5837509..c74ff2548 100644
--- a/lib/storage/storage.js
+++ b/lib/storage/storage.js
@@ -133,24 +133,20 @@ class StorageBase {
             });
     }
 
-    storeItem(item) {
-        logger.error('Trying to store item from base storage' + item);
-        return Promise.reject();
+    async storeItem(item) {
+        throw new Error(`Trying to store item from base storage: ${item}`);
     }
 
-    findUniqueSubhash(hash) {
-        logger.error(`Trying to find unique subhash from base storage ${hash}`);
-        return Promise.reject();
+    async findUniqueSubhash(hash) {
+        throw new Error(`Trying to find unique subhash from base storage ${hash}`);
     }
 
-    expandId(id) {
-        logger.error(`Trying to expand from base storage ${id}`);
-        return Promise.reject();
+    async expandId(id) {
+        throw new Error(`Trying to expand from base storage ${id}`);
     }
 
-    incrementViewCount(id) {
-        logger.error(`Trying to increment view count from base storage ${id}`);
-        return Promise.reject();
+    async incrementViewCount(id) {
+        throw new Error(`Trying to increment view count from base storage ${id}`);
     }
 }
 
diff --git a/lib/tooling/base-tool.js b/lib/tooling/base-tool.js
index 6543defae..7b059d5f8 100644
--- a/lib/tooling/base-tool.js
+++ b/lib/tooling/base-tool.js
@@ -92,7 +92,7 @@ class BaseTool {
         }));
     }
 
-    runTool(compilationInfo, inputFilepath, args, stdin) {
+    async runTool(compilationInfo, inputFilepath, args, stdin) {
         let execOptions = this.getDefaultExecOptions();
         if (inputFilepath) execOptions.customCwd = path.dirname(inputFilepath);
         execOptions.input = stdin;
@@ -102,7 +102,8 @@ class BaseTool {
 
         const exeDir = path.dirname(this.tool.exe);
 
-        return this.exec(this.tool.exe, args, execOptions).then(result => {
+        try {
+            const result = await this.exec(this.tool.exe, args, execOptions);
             const transformedFilepath = result.filenameTransform(inputFilepath);
             return {
                 id: this.tool.id,
@@ -112,10 +113,10 @@ class BaseTool {
                 stderr: utils.parseOutput(result.stderr, transformedFilepath, exeDir),
                 stdout: utils.parseOutput(result.stdout, transformedFilepath, exeDir)
             };
-        }).catch((e) => {
+        } catch (e) {
             logger.error("Error while running tool: ", e);
             return this.createErrorResponse("Error while running tool");
-        });
+        }
     }
 }
 
diff --git a/lib/tooling/clang-tidy-tool.js b/lib/tooling/clang-tidy-tool.js
index 4d6f96361..15c7bf3b0 100644
--- a/lib/tooling/clang-tidy-tool.js
+++ b/lib/tooling/clang-tidy-tool.js
@@ -33,7 +33,7 @@ class ClangTidyTool extends BaseTool {
         super(toolInfo, env);
     }
 
-    runTool(compilationInfo, inputFilepath, args) {
+    async runTool(compilationInfo, inputFilepath, args) {
         const sourcefile = inputFilepath;
         const options = compilationInfo.options;
         const dir = path.dirname(sourcefile);
@@ -44,7 +44,7 @@ class ClangTidyTool extends BaseTool {
         if (wantsFix) {
             args = args.filter(option => !option.includes("-header-filter="));
 
-            const data = fs.readFileSync(sourcefile);
+            const data = await fs.readFile(sourcefile);
             source = data.toString();
         }
 
@@ -63,24 +63,23 @@ class ClangTidyTool extends BaseTool {
         compileFlags = compileFlags.concat(this.tool.options);
 
         // TODO: do we want compile_flags.txt rather than prefixing everything with -extra-arg=
-        return fs.writeFile(
+        await fs.writeFile(
             path.join(dir, "compile_flags.txt"),
             compileFlags.join("\n")
-        ).then(() => super.runTool(compilationInfo, sourcefile, args)
-        ).then((result) => {
-            result.sourcechanged = false;
+        );
+        const result = await super.runTool(compilationInfo, sourcefile, args);
+        result.sourcechanged = false;
 
-            if (wantsFix) {
-                const data = fs.readFileSync(sourcefile);
-                const newsource = data.toString();
-                if (newsource !== source) {
-                    result.sourcechanged = true;
-                    result.newsource = newsource;
-                }
+        if (wantsFix) {
+            const data = await fs.readFile(sourcefile);
+            const newsource = data.toString();
+            if (newsource !== source) {
+                result.sourcechanged = true;
+                result.newsource = newsource;
             }
+        }
 
-            return result;
-        });
+        return result;
     }
 }
 
diff --git a/lib/tooling/llvm-mca-tool.js b/lib/tooling/llvm-mca-tool.js
index 45eff5d7e..5f0c1f10a 100644
--- a/lib/tooling/llvm-mca-tool.js
+++ b/lib/tooling/llvm-mca-tool.js
@@ -33,33 +33,30 @@ class LLVMMcaTool extends BaseTool {
     }
 
     rewriteAsm(asm) {
-        return asm.
-            replace(/.hword\s/gim, '.short ').
-            replace(/OFFSET FLAT:/gim, '').
-            replace(/PTR\s%fs/gim, 'PTR fs');
+        return asm
+            .replace(/.hword\s/gim, '.short ')
+            .replace(/OFFSET FLAT:/gim, '')
+            .replace(/PTR\s%fs/gim, 'PTR fs');
     }
 
     writeAsmFile(data, destination) {
         return fs.writeFile(destination, this.rewriteAsm(data));
     }
 
-    runTool(compilationInfo, inputFilepath, args) {
-        let extraargs = [];
+    async runTool(compilationInfo, inputFilepath, args) {
+        const extraArgs = [];
         if (compilationInfo.filters.intel) {
-            extraargs.push("--x86-asm-syntax=intel");
-            extraargs.push("-output-asm-variant=1");
+            extraArgs.push("--x86-asm-syntax=intel");
+            extraArgs.push("-output-asm-variant=1");
         }
 
         if (compilationInfo.filters.binary) {
-            return new Promise((resolve) => {
-                resolve(this.createErrorResponse("<cannot run analysis on binary>"));
-            });
+            return this.createErrorResponse("<cannot run analysis on binary>");
         }
 
         const rewrittenOutputFilename = compilationInfo.outputFilename + ".mca";
-        return this.writeAsmFile(compilationInfo.asm, rewrittenOutputFilename).then(() => {
-            return super.runTool(compilationInfo, rewrittenOutputFilename, args);
-        });
+        await this.writeAsmFile(compilationInfo.asm, rewrittenOutputFilename);
+        return super.runTool(compilationInfo, rewrittenOutputFilename, args);
     }
 }
 
diff --git a/lib/tooling/pahole-tool.js b/lib/tooling/pahole-tool.js
index a4db61dc8..07c9bde8a 100644
--- a/lib/tooling/pahole-tool.js
+++ b/lib/tooling/pahole-tool.js
@@ -32,17 +32,15 @@ class PaholeTool extends BaseTool {
         super(toolInfo, env);
     }
 
-    runTool(compilationInfo, inputFilepath, args) {
-        if(!compilationInfo.filters.binary)
-        {
+    async runTool(compilationInfo, inputFilepath, args) {
+        if (!compilationInfo.filters.binary) {
             return this.createErrorResponse("Pahole requires a binary output");
         }
 
-        return fs.stat(compilationInfo.executableFilename).then(() => {
+        if (await fs.pathExists(compilationInfo.executableFilename)) {
             return super.runTool(compilationInfo, compilationInfo.executableFilename, args);
-        }).catch(() => {
-            return super.runTool(compilationInfo, compilationInfo.outputFilename, args);
-        });
+        }
+        return super.runTool(compilationInfo, compilationInfo.outputFilename, args);
     }
 }
 


Test patch:
diff --git a/test/base-compiler-tests.js b/test/base-compiler-tests.js
index 94c772294..ae486d487 100644
--- a/test/base-compiler-tests.js
+++ b/test/base-compiler-tests.js
@@ -28,8 +28,9 @@ const chaiAsPromised = require("chai-as-promised");
 const BaseCompiler = require('../lib/base-compiler');
 const CompilationEnvironment = require('../lib/compilation-env');
 const properties = require('../lib/properties');
-const fs = require('fs');
+const fs = require('fs-extra');
 const exec = require('../lib/exec');
+const path = require('path');
 
 chai.use(chaiAsPromised);
 const should = chai.should();
@@ -359,4 +360,28 @@ describe('Compiler execution', function () {
             true);
         result.asm.should.deep.equal("the output");
     });
+
+    it('should run process opt output', async () => {
+        const test = `--- !Missed
+Pass: inline
+Name: NeverInline
+DebugLoc: { File: example.cpp, Line: 4, Column: 21 }
+Function: main
+Args: []
+...
+`;
+        const dirPath = await compiler.newTempDir();
+        const optPath = path.join(dirPath, "temp.out");
+        await fs.writeFile(optPath, test);
+        const a = await compiler.processOptOutput(optPath);
+        a.should.deep.equal([{
+            Args: [],
+            DebugLoc: {Column: 21, File: "example.cpp", Line: 4},
+            Function: "main",
+            Name: "NeverInline",
+            Pass: "inline",
+            displayString: "",
+            optType: "Missed"
+        }]);
+    });
 });
\ No newline at end of file
diff --git a/test/compiler-finder-tests.js b/test/compiler-finder-tests.js
index dc0ef64db..da3727d2a 100644
--- a/test/compiler-finder-tests.js
+++ b/test/compiler-finder-tests.js
@@ -53,6 +53,6 @@ describe('Compiler-finder', function () {
             }
         };
         const finder = new CompilerFinder({}, compilerProps, properties.fakeProps({}), {}, optionsHandler);
-        return Promise.all(finder.getCompilers()).should.eventually.have.lengthOf(2);
+        return finder.getCompilers().should.eventually.have.lengthOf(2);
     })
 });
diff --git a/test/compilers/argument-parsers-tests.js b/test/compilers/argument-parsers-tests.js
index e7b0de66e..8e1e04feb 100644
--- a/test/compilers/argument-parsers-tests.js
+++ b/test/compilers/argument-parsers-tests.js
@@ -57,17 +57,35 @@ describe('option parser', () => {
         return parsers.Base.getOptions(makeCompiler()).should.eventually.deep.equals({});
     });
     it('should parse single-dash options', () => {
-        return parsers.Base.getOptions(makeCompiler("-foo\n")).should.eventually.deep.equals({'-foo': {"description": "", "timesused": 0}});
+        return parsers.Base.getOptions(makeCompiler("-foo\n")).should.eventually.deep.equals({
+            '-foo': {
+                "description": "",
+                "timesused": 0
+            }
+        });
     });
     it('should parse double-dash options', () => {
-        return parsers.Base.getOptions(makeCompiler("--foo\n")).should.eventually.deep.equals({'--foo': {"description": "", "timesused": 0}});
+        return parsers.Base.getOptions(makeCompiler("--foo\n")).should.eventually.deep.equals({
+            '--foo': {
+                "description": "",
+                "timesused": 0
+            }
+        });
     });
     it('should parse stderr options', () => {
-        return parsers.Base.getOptions(makeCompiler("", "--bar=monkey\n")).should.eventually.deep.equals({'--bar=monkey': {"description": "", "timesused": 0}});
+        return parsers.Base.getOptions(makeCompiler("", "--bar=monkey\n")).should.eventually.deep.equals({
+            '--bar=monkey': {
+                "description": "",
+                "timesused": 0
+            }
+        });
     });
     it('handles non-option text', () => {
         return parsers.Base.getOptions(makeCompiler("-foo=123\nthis is a fish\n-badger=123")).should.eventually.deep.equals(
-            {'-foo=123': {"description": "this is a fish", "timesused": 0}, '-badger=123': {"description": "", "timesused": 0}});
+            {
+                '-foo=123': {"description": "this is a fish", "timesused": 0},
+                '-badger=123': {"description": "", "timesused": 0}
+            });
     });
     it('should ignore if errors occur', () => {
         return parsers.Base.getOptions(makeCompiler("--foo\n", "--bar\n", 1)).should.eventually.deep.equals({});
@@ -120,14 +138,24 @@ describe('clang parser', () => {
 
                     result.compiler.options.should.include("-fcolor-diagnostics"),
                     result.compiler.options.should.include("-fno-crash-diagnostics"),
-                    result.compiler.options.should.not.include("-fsave-optimization-record"),
+                    result.compiler.options.should.not.include("-fsave-optimization-record")
                 ]);
             });
     });
 });
 
+describe('pascal parser', () => {
+    it('should handle empty options', () => {
+        return parsers.Pascal.parse(makeCompiler()).should.eventually.satisfy(result => {
+            return Promise.all([
+                result.compiler.options.should.equals('')
+            ]);
+        });
+    });
+});
+
 describe('popular compiler arguments', () => {
-    let compiler = makeCompiler("-fsave-optimization-record\n-x\n-g\n-fcolor-diagnostics\n-O<number> optimization level\n-std=<c++11,c++14,c++17z>")
+    let compiler = makeCompiler("-fsave-optimization-record\n-x\n-g\n-fcolor-diagnostics\n-O<number> optimization level\n-std=<c++11,c++14,c++17z>");
 
     it('should return 5 arguments', () => {
         return parsers.Clang.parse(compiler).then(compiler => {
@@ -142,7 +170,7 @@ describe('popular compiler arguments', () => {
                     })
                 ]);
             });
-        })
+        });
     });
 
     it('should return arguments except the ones excluded', () => {
@@ -158,7 +186,7 @@ describe('popular compiler arguments', () => {
                     })
                 ]);
             });
-        })
+        });
     });
 
     it('should be able to exclude special params with assignments', () => {
@@ -173,6 +201,6 @@ describe('popular compiler arguments', () => {
                     })
                 ]);
             });
-        })
+        });
     });
 });