Browse Source

Merge branch 'master' into monaco

dev/git-series/gccdum
Matt Godbolt 6 years ago
parent
commit
840d0a3504
  1. 7
      Makefile
  2. 56
      README.md
  3. 73
      app.js
  4. 2
      c-preload/compiler-wrapper
  5. 21
      etc/config/c++.amazon.properties
  6. 4
      etc/config/c++.lud-mgodbolt01.properties
  7. 2
      etc/config/compiler-explorer.amazon.properties
  8. 2
      etc/config/compiler-explorer.defaults.properties
  9. 8
      lib/asm-cl.js
  10. 29
      lib/base-compiler.js
  11. 4
      lib/compilation-env.js
  12. 84
      lib/compile-handler.js
  13. 9
      lib/utils.js
  14. 2
      package.json
  15. 41
      static/alert.js
  16. 36
      static/async-get.js
  17. 9
      static/compiler.js
  18. 11
      static/editor.js
  19. 6
      static/explorer.css
  20. 19
      static/index.html
  21. 14
      static/main.js
  22. 4
      static/options.js
  23. 13
      static/url.js
  24. 90
      test/cases/cl-main-opt-out.asm
  25. 94
      test/cases/cl-main-opt-out.directives.labels.comments.json
  26. 2
      test/filter-tests.js

7
Makefile

@ -20,10 +20,9 @@ endif
.PHONY: dist lint prereqs node_modules bower_modules
prereqs: optional-d-support optional-rust-support node_modules c-preload bower_modules
ifneq "" "$(shell which gdc)"
optional-d-support:
$(MAKE) -C d
else ifneq "" "$(shell which ${DMD})"
GDC?=gdc
DMD?=dmd
ifneq "" "$(shell which $(GDC) 2>/dev/null || which $(DMD) 2>/dev/null)"
optional-d-support:
$(MAKE) -C d
else

56
README.md

@ -37,3 +37,59 @@ Feel free to raise an issue on [github](https://github.com/mattgodbolt/compiler-
Compiler Explorer is maintained by [Matt Godbolt](http://xania.org). Multiple compiler and difference view was
implemented by [Gabriel Devillers](https://github.com/voxelf).
### RESTful API
There's a simple restful API that can be used to do compiles to asm and to list compilers. In general
all handlers live in `/api/*` endpoints, and will accept JSON or text in POSTs, and will return text responses
or JSON responses depending on the request's `Accept` header.
At a later date there may be some form of rate-limiting: currently requests will be queued and dealt with
exactly like interactive requests on the main site. Authentication might be required at some point in the
future (for the main Compiler Explorer site anyway).
The following endpoints are defined:
#### `GET /api/compilers` - return a list of compilers
Returns a list of compilers. In text form, there's a simple formatting of the ID of the compiler and its
description. In JSON, all the information is returned as an array of compilers, with the `id` key being the
primary identifier of each compiler.
#### `POST /api/<compiler-id>/compiler` - perform a compilation
To specify a compilation request as a JSON document, post it as the appropriate type and send an object of
the form: `{'source': 'source to compile', 'options': 'compiler flags', 'filters': {'filter': true}}`. The filters are an JSON object with true/false. If not supplied, defaults are used. If supplied, the filters are used
as-is.
A text compilation request has the source as the body of the post, and uses query parameters to pass the
options and filters. Filters are supplied as a comma-separated string. Use the query parameter `filters=XX`
to set the filters directly, else `addFilters=XX` to add a filter to defaults, or `removeFilters` to remove from defaults. Compiler parameters should be passed as `options=-O2` and default to empty.
Filters include `binary`, `labels`, `intel`, `comments` and `directives` and correspond to the UI buttons on
the HTML version.
The text request is designed for simplicity for command-line clients like `curl`:
```bash
$ curl 'https://gcc.godbolt.org/api/compiler/g63/compile?options=-Wall' --data-binary 'int foo() { return 1; }'
# Compilation provided by Compiler Explorer at gcc.godbolt.org
foo():
push rbp
mov rbp, rsp
mov eax, 1
pop rbp
ret
```
If JSON is present in the request's `Accept` header, the compilation results are of the form:
```json
{
code: 0 if successful, else compiler return code,
stdout: [ { text: "Output",
(optional) tag: {line: source line, text: "parsed error for that line"} } ],
stderr: (as above),
asm: [ { text: "assembly text", source: source line number or null if none } ]
}
```

73
app.js

@ -138,8 +138,8 @@ fileSources.forEach(function (source) {
});
var clientOptionsHandler = new ClientOptionsHandler(fileSources);
var apiHandler = new ApiHandler();
var compileHandler = new CompileHandler(gccProps, compilerProps);
var apiHandler = new ApiHandler(compileHandler);
// auxiliary function used in clientOptionsHandler
function compareOn(key) {
@ -386,27 +386,32 @@ function findCompilers() {
});
}
// Instantiate a function that writes information on the compiler,
// in JSON format, for ease of external listing.
function ApiHandler() {
var reply = "";
function ApiHandler(compileHandler) {
this.compilers = [];
this.compileHandler = compileHandler;
this.setCompilers = function (compilers) {
reply = JSON.stringify(compilers);
this.compilers = compilers;
};
this.handler = function apiHandler(req, res, next) {
var bits = req.url.split("/");
if (bits.length !== 2 || req.method !== "GET") return next();
switch (bits[1]) {
default:
next();
break;
case "compilers":
res.set('Content-Type', 'application/json');
res.end(reply);
break;
this.handler = express.Router();
this.handler.get('/compilers', _.bind(function (req, res, next) {
if (req.accepts(['text', 'json']) == 'json') {
res.set('Content-Type', 'application/json');
res.end(JSON.stringify(this.compilers));
} else {
res.set('Content-Type', 'text/plain');
var title = 'Compiler Name';
var maxLength = _.max(_.pluck(_.pluck(this.compilers, 'id').concat([title]), 'length'));
res.write(padRight(title, maxLength) + ' | Description\n');
res.end(_.map(this.compilers, function (compiler) {
return padRight(compiler.id, maxLength) + ' | ' + compiler.name + '\n';
}).join("\n"));
}
};
}, this));
this.handler.param('compiler', _.bind(function (req, res, next, compilerName) {
req.compiler = compilerName;
next();
}, this));
this.handler.post('/compiler/:compiler/compile', this.compileHandler.handler);
}
function shortUrlHandler(req, res, next) {
@ -455,7 +460,6 @@ function embeddedHandler(req, res, next) {
res.end();
}
findCompilers()
.then(function (compilers) {
var prevCompilers;
@ -485,7 +489,6 @@ findCompilers()
var webServer = express(),
sFavicon = require('serve-favicon'),
sStatic = require('serve-static'),
bodyParser = require('body-parser'),
morgan = require('morgan'),
compression = require('compression'),
@ -502,28 +505,38 @@ findCompilers()
.use(morgan('combined', {stream: logger.stream}))
.use(compression())
.use(sFavicon(staticDir + '/favicon.ico'))
.use('/v', sStatic(staticDir + '/v', {maxAge: Infinity}))
.use(sStatic(staticDir, {maxAge: staticMaxAgeSecs * 1000}));
.use('/v', express.static(staticDir + '/v', {maxAge: Infinity, index: false}))
.use(express.static(staticDir, {maxAge: staticMaxAgeSecs * 1000}));
if (archivedVersions) {
// The archived versions directory is used to serve "old" versioned data during updates. It's expected
// to contain all the SHA-hashed directories from previous versions of Compiler Explorer.
logger.info(" serving archived versions from", archivedVersions);
webServer.use('/v', sStatic(archivedVersions, {maxAge: Infinity}));
webServer.use('/v', express.static(archivedVersions, {maxAge: Infinity, index: false}));
}
webServer
.use(bodyParser.json({limit: gccProps('bodyParserLimit', '1mb')}))
.use(bodyParser.text({
limit: gccProps('bodyParserLimit', '1mb'), type: function () {
return true;
}
}))
.use(restreamer())
.get('/client-options.json', clientOptionsHandler.handler)
.use('/source', getSource)
.use('/api', apiHandler.handler)
.use('/g', shortUrlHandler)
.use('/e', embeddedHandler)
.post('/compile', compileHandler.handler) // used inside static/compiler.js
.post('/diff', diffHandler); // used inside static/compiler.js
.post('/compile', compileHandler.handler)
.post('/diff', diffHandler);
logger.info("=======================================");
webServer.on('error', function (err) {
logger.error('Caught error:', err, "(in web error handler; continuing)");
});
webServer.listen(port, hostname);
}).catch(function (err) {
logger.error("Error: " + err);
process.exit(1);
});
})
.catch(function (err) {
logger.error("Promise error:", err, "(shutting down)");
process.exit(1);
});

2
c-preload/compiler-wrapper

@ -15,7 +15,7 @@ ALLOWED_FOR_READ=${ALLOWED_FOR_READ}:/usr/share/fonts:/etc/passwd
# Last-gasp limits
ulimit -m $((512 * 1024)) # RSS limit in K
ulimit -t 5 # CPU time in seconds
ulimit -t 10 # CPU time in seconds
ulimit -v $((2 * 1024 * 1024)) # virtual RAM limit in K
"$@"

21
etc/config/c++.amazon.properties

@ -46,11 +46,11 @@ compiler.g62.exe=/opt/compiler-explorer/gcc-6.2.0/bin/g++
compiler.g62.name=x86-64 gcc 6.2
compiler.g63.exe=/opt/compiler-explorer/gcc-6.3.0/bin/g++
compiler.g63.name=x86-64 gcc 6.3
compiler.g7snapshot.exe= /opt/compiler-explorer/gcc-7-20161113/bin/g++
compiler.g7snapshot.exe= /opt/compiler-explorer/gcc-7-snapshot/bin/g++
compiler.g7snapshot.name=x86-64 gcc 7 (snapshot)
# Clang for x86
group.clang.compilers=clang350:clang351:clang37x:clang36x:clang371:clang380:clang381:clang390
group.clang.compilers=clang350:clang351:clang37x:clang36x:clang371:clang380:clang381:clang390:clang391:clang_trunk
group.clang.intelAsm=-masm=intel
group.clang.options=--gcc-toolchain=/opt/compiler-explorer/gcc-6.2.0
compiler.clang350.exe=/usr/bin/clang++-3.5
@ -71,6 +71,10 @@ compiler.clang381.exe=/opt/compiler-explorer/clang+llvm-3.8.1-x86_64-linux-gnu-u
compiler.clang381.name=x86-64 clang 3.8.1
compiler.clang390.exe=/opt/compiler-explorer/clang+llvm-3.9.0-x86_64-linux-gnu-ubuntu-16.04/bin/clang++
compiler.clang390.name=x86-64 clang 3.9.0
compiler.clang391.exe=/opt/compiler-explorer/clang-3.9.1/bin/clang++
compiler.clang391.name=x86-64 clang 3.9.1
compiler.clang_trunk.exe=/opt/compiler-explorer/clang-trunk/bin/clang++
compiler.clang_trunk.name=x86-64 clang (trunk)
# icc for x86
group.icc.compilers=icc16:icc17
@ -134,8 +138,11 @@ compiler.cl19_arm.name=ARM CL 19 RC
#################################
# ELLCC
group.ellcc.compilers=elcc0133:elcc0134
compiler.elcc0133.exe=/opt/compiler-explorer/ellcc-0.1.33/bin/ecc++
compiler.elcc0133.name=elcc 0.1.33
compiler.elcc0134.exe=/opt/compiler-explorer/ellcc-0.1.34/bin/ecc++
compiler.elcc0134.name=elcc 0.1.34
group.ellcc.compilers=ellcc0133:ellcc0134
compiler.ellcc0133.exe=/opt/compiler-explorer/ellcc-0.1.33/bin/ecc++
compiler.ellcc0133.name=ellcc 0.1.33
compiler.ellcc0134.exe=/opt/compiler-explorer/ellcc-0.1.34/bin/ecc++
compiler.ellcc0134.name=ellcc 0.1.34
# Handle old URLs pre-typo fixup
compiler.ellcc0133.alias=elcc0133
compiler.ellcc0134.alias=elcc0134

4
etc/config/c++.lud-mgodbolt01.properties

@ -1,7 +1,7 @@
# Default settings for GCC Explorer.
defaultCompiler=g52
#compilers=g44:g45:g46:clang35:g51:g52:gdef:msp430g453:cl
compilers=&gcc:&clang:&windows
compilers=g44:g45:g46:clang35:g51:g52:gdef:msp430g453:cl
#compilers=&gcc:&clang:&windows
group.gcc.compilers=gdef:g62
compiler.gdef.exe=/usr/bin/g++
compiler.gdef.name=Default G++

2
etc/config/compiler-explorer.amazon.properties

@ -1,6 +1,6 @@
# Settings overridden for Amazon EC2 instances that power http://*.godbolt.org/
googleApiKey=AIzaSyAaz35KJv8DA0ABoime0fEIh32NmbyYbcQ
compileTimeoutMs=5000
compileTimeoutMs=7500
compiler-wrapper=./c-preload/compiler-wrapper
max-asm-size=8388608
staticMaxAgeSecs=30

2
etc/config/compiler-explorer.defaults.properties

@ -1,6 +1,6 @@
# Default settings for GCC Explorer.
port=10240
compileTimeoutMs=3000
compileTimeoutMs=10000
defaultSource=builtin
cacheMb=50
language=C++

8
lib/asm-cl.js

@ -34,12 +34,12 @@ var gccExplorerDir = /\\compiler-explorer-compiler/; // has to match part of the
// * optional leading whitespace
// * middle part
// * comment part
var parseRe = /^(\s*)([^;]*)(;.*)*$/;
var isProc = /.*PROC$/;
var isEndp = /.*ENDP$/;
var parseRe = /^(\s*)([^;]*)(;.*)?$/;
var isProc = /.*PROC\s*$/;
var isEndp = /.*ENDP\s*$/;
var constDef = /^([a-zA-Z_$@][a-zA-Z_$@0-9.]*)\s*=.*$/;
var labelFind = /[.a-zA-Z_@$][a-zA-Z_$@0-9.]*/g;
var labelDef = /^(.*):$/;
var labelDef = /^(.*):\s*$/;
// Anything identifier-looking with a "@@" in the middle, and a comment at the end
// is treated as a mangled name. The comment will be used to replace the identifier.
var mangledIdentifier = /\?[^ ]+@@[^ |]+/;

29
lib/base-compiler.js

@ -31,6 +31,7 @@ var child_process = require('child_process'),
utils = require('./utils'),
quote = require('shell-quote'),
_ = require('underscore-node'),
treeKill = require('tree-kill'),
logger = require('./logger').logger;
function Compile(compiler, env) {
@ -236,6 +237,10 @@ Compile.prototype.checkOptions = function (options) {
return null;
};
// This check for arbitrary user-controlled preprocessor inclusions
// can be circumvented in more than one way. The goal here is to respond
// to simple attempts with a clear diagnostic; the service still needs to
// assume that malicious actors can make the compiler open arbitrary files.
Compile.prototype.checkSource = function (source) {
var re = /^\s*#\s*i(nclude|mport)(_next)?\s+["<"](\/|.*\.\.)/;
var failed = [];
@ -265,12 +270,19 @@ Compile.prototype.exec = function (command, args, options) {
env: env,
detached: process.platform == 'linux'
});
var running = true;
function kill() {
if (running) treeKill(child.pid);
}
var stderr = "";
var stdout = "";
var timeout;
if (timeoutMs) timeout = setTimeout(function () {
logger.warn("Timeout for", command, args, "after", timeoutMs, "ms");
okToCache = false;
child.kill();
kill();
stderr += "\nKilled - processing time exceeded";
}, timeoutMs);
var truncated = false;
@ -279,7 +291,7 @@ Compile.prototype.exec = function (command, args, options) {
if (stdout.length > maxOutput) {
stdout += "\n[Truncated]";
truncated = true;
child.kill();
kill();
return;
}
stdout += data;
@ -289,7 +301,7 @@ Compile.prototype.exec = function (command, args, options) {
if (stderr.length > maxOutput) {
stderr += "\n[Truncated]";
truncated = true;
child.kill();
kill();
return;
}
stderr += data;
@ -297,6 +309,7 @@ Compile.prototype.exec = function (command, args, options) {
child.on('exit', function (code) {
logger.debug({type: 'exited', code: code});
if (timeout !== undefined) clearTimeout(timeout);
running = false;
});
return new Promise(function (resolve, reject) {
child.on('error', function (e) {
@ -376,4 +389,14 @@ Compile.prototype.getInfo = function () {
return this.compiler;
};
Compile.prototype.getDefaultFilters = function () {
// TODO; propagate to UI?
return {
intel: true,
commentOnly: true,
directives: true,
labels: true
};
};
module.exports = Compile;

4
lib/compilation-env.js

@ -100,6 +100,10 @@ CompilationEnvironment.prototype.enqueue = function (job) {
return this.compileQueue.add(job);
};
CompilationEnvironment.prototype.isBusy = function () {
return this.compileQueue.getPendingLength() > 0 || this.compileQueue.getQueueLength() > 0;
};
CompilationEnvironment.prototype.findBadOptions = function (options) {
return _.filter(options, function (option) {
return !option.match(this.okOptions) || option.match(this.badOptions);

84
lib/compile-handler.js

@ -33,26 +33,28 @@ var child_process = require('child_process'),
temp.track();
function periodicCleanup() {
temp.cleanup(function (err, stats) {
if (err) logger.error("Error cleaning directories: ", err);
if (stats) logger.debug("Directory cleanup stats:", stats);
});
}
var oneTimeInit = false;
function initialise(gccProps, compilerProps) {
function initialise(gccProps, compilerEnv) {
if (oneTimeInit) return;
oneTimeInit = true;
var tempDirCleanupSecs = gccProps("tempDirCleanupSecs", 600);
logger.info("Cleaning temp dirs every " + tempDirCleanupSecs + " secs");
setInterval(periodicCleanup, tempDirCleanupSecs * 1000);
setInterval(function () {
if (compilerEnv.isBusy()) {
logger.warn("Skipping temporary file clean up as compiler environment is busy");
return;
}
temp.cleanup(function (err, stats) {
if (err) logger.error("Error cleaning directories: ", err);
if (stats) logger.debug("Directory cleanup stats:", stats);
});
}, tempDirCleanupSecs * 1000);
}
function CompileHandler(gccProps, compilerProps) {
initialise(gccProps, compilerProps);
this.compilersById = {};
this.compilerEnv = new CompilationEnvironment(gccProps, compilerProps);
initialise(gccProps, this.compilerEnv);
this.factories = {};
this.create = function (compiler) {
@ -85,9 +87,37 @@ function CompileHandler(gccProps, compilerProps) {
var proxy = httpProxy.createProxyServer({});
this.handler = _.bind(function compile(req, res, next) {
var compiler = this.compilersById[req.body.compiler];
if (!compiler) return next();
var source, options, filters, compiler;
if (req.is('json')) {
// JSON-style request
compiler = this.compilersById[req.compiler || req.body.compiler];
if (!compiler) return next();
source = req.body.source;
options = req.body.options;
filters = req.body.filters || compiler.getDefaultFilters();
} else {
// API-style
compiler = this.compilersById[req.compiler];
if (!compiler) return next();
source = req.body;
options = req.query.options;
// By default we get the default filters.
filters = compiler.getDefaultFilters();
// If specified exactly, we'll take that with ?filters=a,b,c
if (req.query.filters) {
filters = _.object(_.map(req.query.filters.split(","), function (filter) {
return [filter, true];
}));
}
// Add a filter. ?addFilters=binary
_.each((req.query.addFilters || "").split(","), function (filter) {
filters[filter] = true;
});
// Remove a filter. ?removeFilter=intel
_.each((req.query.removeFilters || "").split(","), function (filter) {
delete filters[filter];
});
}
var remote = compiler.getRemote();
if (remote) {
proxy.web(req, res, {target: remote}, function (e) {
@ -96,23 +126,39 @@ function CompileHandler(gccProps, compilerProps) {
});
return;
}
var source = req.body.source;
var options = req.body.options || '';
if (source === undefined) {
return next(new Error("Bad request"));
}
options = _.chain(quote.parse(options)
options = _.chain(quote.parse(options || '')
.map(function (x) {
if (typeof(x) == "string") return x;
return x.pattern;
}))
.filter(_.identity)
.value();
var filters = req.body.filters;
function textify(array) {
return _.pluck(array || [], 'text').join("\n");
}
compiler.compile(source, options, filters).then(
function (result) {
res.set('Content-Type', 'application/json');
res.end(JSON.stringify(result));
if (req.accepts(['text', 'json']) === 'json') {
res.set('Content-Type', 'application/json');
res.end(JSON.stringify(result));
} else {
res.set('Content-Type', 'text/plain');
try {
res.write("# Compilation provided by Compiler Explorer at " + req.get('Host') + "\n");
res.write(textify(result.asm));
if (result.code !== 0) res.write("\n# Compiler exited with result code " + result.code);
if (!_.isEmpty(result.stdout)) res.write("\nStandard out:\n" + textify(result.stdout));
if (!_.isEmpty(result.stderr)) res.write("\nStandard error:\n" + textify(result.stderr));
} catch (ex) {
re.write("Error handling request: " + ex);
}
res.end('\n');
}
},
function (error) {
logger.error("Error: " + error);

9
lib/utils.js

@ -66,4 +66,11 @@ function parseOutput(lines, inputFilename) {
return result;
}
exports.parseOutput = parseOutput;
exports.parseOutput = parseOutput;
function padRight(name, len) {
while (name.length < len) name = name + ' ';
return name;
}
exports.padRight = padRight;

2
package.json

@ -28,8 +28,8 @@
"promise-queue": "2.1.x",
"nopt": "3.0.x",
"serve-favicon": "2.3.x",
"serve-static": "1.10.x",
"temp": "0.8.x",
"tree-kill": "1.1.x",
"underscore-node": "*",
"shell-quote": "1.6.x",
"winston": "2.2.x"

41
static/alert.js

@ -0,0 +1,41 @@
// Copyright (c) 2012-2017, Matt Godbolt
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
define(function (require) {
var $ = require('jquery');
function Alert() {
this.modal = $('#alert');
this.title = this.modal.find('.modal-title');
this.body = this.modal.find('.modal-body');
}
Alert.prototype.alert = function (title, body) {
this.title.html(title);
this.body.html(body);
this.modal.modal();
};
return Alert;
});

36
static/async-get.js

@ -0,0 +1,36 @@
// Copyright (c) 2012-2017, Matt Godbolt
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
define({
load: function (name, req, onLoad, config) {
if (config.isBuild) return onLoad(null); // optimizer build
req(['jquery'], function ($) {
$.ajax({type: "GET", url: name}).done(function (response) {
onLoad(response);
});
});
}
});

9
static/compiler.js

@ -40,7 +40,11 @@ define(function (require) {
var options = require('options');
var compilers = options.compilers;
var compilersById = _.object(_.pluck(compilers, "id"), compilers);
var compilersById = {};
_.forEach(compilers, function(compiler) {
compilersById[compiler.id] = compiler;
if (compiler.alias) compilersById[compiler.alias] = compiler;
});
var Cache = new LruCache({
max: 200 * 1024,
length: function (n) {
@ -149,7 +153,6 @@ define(function (require) {
this.saveState();
}
// TODO: need to call resize if either .top-bar or .bottom-bar resizes, which needs some work.
// Issue manifests if you make a window where one compiler is small enough that the buttons spill onto two lines:
// reload the page and the bottom-bar is off the bottom until you scroll a tiny bit.
@ -212,7 +215,7 @@ define(function (require) {
}, this), 500);
$.ajax({
type: 'POST',
url: '/compile',
url: '/api/compiler/' + request.compiler + '/compile',
dataType: 'json',
contentType: 'application/json',
data: jsonRequest,

11
static/editor.js

@ -146,14 +146,19 @@ define(function (require) {
this.eventHub.on('compileResult', this.onCompileResponse, this);
this.eventHub.on('selectLine', this.onSelectLine, this);
var compilerConfig = Components.getCompiler(this.id);
// NB a new compilerConfig needs to be created every time; else the state is shared
// between all compilers created this way. That leads to some nasty-to-find state
// bugs e.g. https://github.com/mattgodbolt/compiler-explorer/issues/225
var compilerConfig = _.bind(function () {
return Components.getCompiler(this.id);
}, this);
this.container.layoutManager.createDragSource(
this.domRoot.find('.btn.add-compiler'), compilerConfig);
this.domRoot.find('.btn.add-compiler'), compilerConfig());
this.domRoot.find('.btn.add-compiler').click(_.bind(function () {
var insertPoint = hub.findParentRowOrColumn(this.container) ||
this.container.layoutManager.root.contentItems[0];
insertPoint.addChild(compilerConfig);
insertPoint.addChild(compilerConfig());
}, this));
Sharing.initShareButton(this.domRoot.find('.share'), container.layoutManager);

6
static/explorer.css

@ -212,4 +212,10 @@ pre.content {
.compile-time {
font-size: x-small;
font-style: italic;
}
.url-parse-info {
font-size: small;
margin-top: 1em;
font-style: italic;
}

19
static/index.html

@ -243,5 +243,24 @@ int square(int num) {
<!-- /.modal-dialog -->
</div>
<div class="modal fade gl_keep" id="alert">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Something alert worthy</h4>
</div>
<div class="modal-body">
</div>
<!-- model-body -->
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
</body>
</html>

14
static/main.js

@ -63,6 +63,7 @@ define(function (require) {
var clipboard = require('clipboard');
var Hub = require('hub');
var Raven = require('raven-js');
var Alert = require('alert');
function start() {
analytics.initialise();
@ -70,6 +71,7 @@ define(function (require) {
var options = require('options');
$('.language-name').text(options.language);
var alert = new Alert();
var safeLang = options.language.toLowerCase().replace(/[^a-z_]+/g, '');
var defaultSrc = $('.template .lang.' + safeLang).text().trim();
@ -86,7 +88,17 @@ define(function (require) {
var config;
if (!options.embedded) {
config = url.deserialiseState(window.location.hash.substr(1));
var serializedState = window.location.hash.substr(1);
if (serializedState) {
try {
config = url.deserialiseState(serializedState);
} catch (exception) {
alert.alert("Unable to parse URL",
"<div>Compiler Explorer was unable to parse the URL hash. " +
"Please check it and try again.</div>" +
"<div class='url-parse-info'>" + exception + "</div>");
}
}
if (config) {
// replace anything in the default config with that from the hash
config = _.extend(defaultConfig, config);

4
static/options.js

@ -25,8 +25,8 @@
define(function (require) {
"use strict";
var $ = require('jquery');
var options = $.ajax({type: "GET", url: 'client-options.json', async: false}).responseJSON;
var options = require('./async-get!client-options.json');
options.embedded = window.location.pathname === "/embed.html";
return options;
});

13
static/url.js

@ -34,7 +34,7 @@ define(function (require) {
function convertOldState(state) {
var sc = state.compilers[0];
if (!sc) return false;
if (!sc) throw new Error("Unable to determine compiler from old state");
var content = [];
var source;
if (sc.sourcez) {
@ -68,7 +68,7 @@ define(function (require) {
state = GoldenLayout.unminifyConfig(state);
break;
default:
return false;
throw new Error("Invalid version '" + state.version + "'");
}
return state;
}
@ -83,21 +83,24 @@ define(function (require) {
function deserialiseState(stateText) {
var state;
var exception;
try {
state = unrisonify(stateText);
if (state && state.z) {
state = unrisonify(lzstring.decompressFromBase64(state.z));
}
} catch (ignored) {
} catch (ex) {
exception = ex;
}
if (!state) {
try {
state = $.parseJSON(decodeURIComponent(stateText));
} catch (ignored) {
} catch (ex) {
if (!exception) exception = ex;
}
}
if (!state && exception) throw exception;
return loadState(state);
}

90
test/cases/cl-main-opt-out.asm

@ -0,0 +1,90 @@
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24224.0
include listing.inc
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC main
PUBLIC ??$make_array@$00$02$04$01$02$04$05$03$07$08$01@@YA?AV?$array@H$0L@@std@@XZ ; make_array<1,3,5,2,3,5,6,4,8,9,2>
PUBLIC ??A?$array@H$0L@@std@@QEBAAEBH_K@Z ; std::array<int,11>::operator[]
; Function compile flags: /Ogtpy
; File z:\home\mgodbolt\apps\windows\14.0.24224-pre\lib\native\include\array
; COMDAT ??A?$array@H$0L@@std@@QEBAAEBH_K@Z
_TEXT SEGMENT
this$ = 8
_Pos$ = 16
??A?$array@H$0L@@std@@QEBAAEBH_K@Z PROC ; std::array<int,11>::operator[], COMDAT
; 167 : #if _ITERATOR_DEBUG_LEVEL == 0
; 168 : return (_Elems[_Pos]);
00000 48 8d 04 91 lea rax, QWORD PTR [rcx+rdx*4]
; 169 :
; 170 : #else /* _ITERATOR_DEBUG_LEVEL == 0 */
; 171 : return (_Size <= _Pos
; 172 : ? (_Bad_subscript(), _Elems[_Pos])
; 173 : : _Elems[_Pos]);
; 174 : #endif /* _ITERATOR_DEBUG_LEVEL == 0 */
; 175 : }
00004 c3 ret 0
??A?$array@H$0L@@std@@QEBAAEBH_K@Z ENDP ; std::array<int,11>::operator[]
_TEXT ENDS
; Function compile flags: /Ogtpy
; File z:\tmp\example.cpp
; COMDAT ??$make_array@$00$02$04$01$02$04$05$03$07$08$01@@YA?AV?$array@H$0L@@std@@XZ
_TEXT SEGMENT
$T1 = 8
??$make_array@$00$02$04$01$02$04$05$03$07$08$01@@YA?AV?$array@H$0L@@std@@XZ PROC ; make_array<1,3,5,2,3,5,6,4,8,9,2>, COMDAT
; 6 : return std::array<int, sizeof...(vars)>{vars...};
00000 c7 01 01 00 00
00 mov DWORD PTR [rcx], 1
00006 48 8b c1 mov rax, rcx
00009 c7 41 04 03 00
00 00 mov DWORD PTR [rcx+4], 3
00010 c7 41 08 05 00
00 00 mov DWORD PTR [rcx+8], 5
00017 c7 41 0c 02 00
00 00 mov DWORD PTR [rcx+12], 2
0001e c7 41 10 03 00
00 00 mov DWORD PTR [rcx+16], 3
00025 c7 41 14 05 00
00 00 mov DWORD PTR [rcx+20], 5
0002c c7 41 18 06 00
00 00 mov DWORD PTR [rcx+24], 6
00033 c7 41 1c 04 00
00 00 mov DWORD PTR [rcx+28], 4
0003a c7 41 20 08 00
00 00 mov DWORD PTR [rcx+32], 8
00041 c7 41 24 09 00
00 00 mov DWORD PTR [rcx+36], 9
00048 c7 41 28 02 00
00 00 mov DWORD PTR [rcx+40], 2
; 7 : }
0004f c3 ret 0
??$make_array@$00$02$04$01$02$04$05$03$07$08$01@@YA?AV?$array@H$0L@@std@@XZ ENDP ; make_array<1,3,5,2,3,5,6,4,8,9,2>
_TEXT ENDS
; Function compile flags: /Ogtpy
; File z:\tmp\example.cpp
; COMDAT main
_TEXT SEGMENT
main PROC ; COMDAT
; 11 : constexpr auto a = make_array<1,3,5,2,3,5,6,4,8,9,2>();
; 12 :
; 13 : return a[2];
00000 b8 05 00 00 00 mov eax, 5
; 14 : }
00005 c3 ret 0
main ENDP
_TEXT ENDS
END

94
test/cases/cl-main-opt-out.directives.labels.comments.json

@ -0,0 +1,94 @@
[
{
"source": null,
"text": "std::array<int,11>::operator[], COMDAT PROC"
},
{
"source": null,
"text": " lea rax, QWORD PTR [rcx+rdx*4]"
},
{
"source": null,
"text": " ret 0"
},
{
"source": null,
"text": "std::array<int,11>::operator[] ENDP"
},
{
"source": null,
"text": "make_array<1,3,5,2,3,5,6,4,8,9,2>, COMDAT PROC"
},
{
"source": null,
"text": " mov DWORD PTR [rcx], 1"
},
{
"source": null,
"text": " mov rax, rcx"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+4], 3"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+8], 5"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+12], 2"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+16], 3"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+20], 5"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+24], 6"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+28], 4"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+32], 8"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+36], 9"
},
{
"source": null,
"text": " mov DWORD PTR [rcx+40], 2"
},
{
"source": null,
"text": " ret 0"
},
{
"source": null,
"text": "make_array<1,3,5,2,3,5,6,4,8,9,2> ENDP"
},
{
"source": null,
"text": "main PROC; COMDAT"
},
{
"source": null,
"text": " mov eax, 5"
},
{
"source": null,
"text": " ret 0"
},
{
"source": null,
"text": "main ENDP"
}
]

2
test/filter-tests.js

@ -98,6 +98,7 @@ function testFilter(filename, suffix, filters) {
// bless("cases/cl-regex.asm", "cases/cl-regex.asm.dlcb.json", {directives: true, labels: true, commentOnly: true, binary:true});
// bless("cases/cl-maxarray.asm", "cases/cl-maxarray.asm.dlcb.json", {directives: true, labels: true, commentOnly: true, binary:true});
// bless("cases/cl64-sum.asm", "cases/cl64-sum.asm.dlcb.json", {directives: true, labels: true, commentOnly: true, binary:true});
// bless("cases/cl-main-opt-out.asm", "cases/cl-main-opt-out.directives.labels.comments.json", {directives: true, labels: true, commentOnly: true});
// bless("cases/avr-loop.asm", "cases/avr-loop.asm.directives.labels.comments.json", {directives: true, labels: true, commentOnly: true});
// bless("cases/bug-192.asm", "cases/bug-192.asm.directives.labels.comments.json", {directives: true, labels: true, commentOnly: true});
// bless("cases/arm-moose.asm", "cases/arm-moose.asm.directives.labels.comments.json", {directives: true, labels: true, commentOnly: true});
@ -133,3 +134,4 @@ describe('Filter test cases', function () {
});
});
});

Loading…
Cancel
Save