| // Copyright (c) 2012, 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. |
| |
| function parseLines(lines, callback) { |
| var re = /^\<stdin\>:([0-9]+):([0-9]+):\s+(.*)/; |
| $.each(lines.split('\n'), function(_, line) { |
| line = line.trim(); |
| if (line != "") { |
| var match = line.match(re); |
| if (match) { |
| callback(parseInt(match[1]), match[3]); |
| } else { |
| callback(null, line); |
| } |
| } |
| }); |
| } |
| function clearBackground(cm) { |
| for (var i = 0; i < cm.lineCount(); ++i) { |
| cm.setLineClass(i, null, null); |
| } |
| } |
| |
| function Compiler(domRoot, origFilters) { |
| var compilersByExe = {}; |
| var pendingTimeout = null; |
| var asmCodeMirror = null; |
| var cppEditor = null; |
| var lastRequest = null; |
| var currentAssembly = null; |
| var filters = origFilters; |
| var ignoreChanges = true; // Horrible hack to avoid onChange doing anything on first starting, ie before we've set anything up. |
| |
| cppEditor = CodeMirror.fromTextArea(domRoot.find(".editor textarea")[0], { |
| lineNumbers: true, |
| matchBrackets: true, |
| useCPP: true, |
| mode: "text/x-c++src", |
| onChange: onChange |
| }); |
| asmCodeMirror = CodeMirror.fromTextArea(domRoot.find(".asm textarea")[0], { |
| lineNumbers: true, |
| matchBrackets: true, |
| mode: "text/x-asm", |
| readOnly: true |
| }); |
| |
| if (window.localStorage['code']) cppEditor.setValue(window.localStorage['code']); |
| domRoot.find('.compiler').change(onCompilerChange); |
| domRoot.find('.compiler_options').change(onChange).keyup(onChange); |
| ignoreChanges = false; |
| |
| if (window.localStorage['compilerOptions']) { |
| domRoot.find('.compiler_options').val(window.localStorage['compilerOptions']); |
| } |
| |
| function onCompileResponse(data) { |
| var stdout = data.stdout || ""; |
| var stderr = data.stderr || ""; |
| if (data.code == 0) { |
| stdout += "\nCompiled ok"; |
| } else { |
| stderr += "\nCompilation failed"; |
| } |
| $('.result .output :visible').remove(); |
| var highlightLine = (data.asm == null); |
| for (var i = 0; i < cppEditor.lineCount(); ++i) cppEditor.setMarker(i); |
| parseLines(stderr + stdout, function(lineNum, msg) { |
| var elem = $('.result .output .template').clone().appendTo('.result .output').removeClass('template'); |
| if (lineNum) { |
| cppEditor.setMarker(lineNum - 1, null, "error"); |
| elem.html($('<a href="#">').append(lineNum + " : " + msg)).click(function() { |
| cppEditor.setSelection({line: lineNum - 1, ch: 0}, {line: lineNum, ch: 0}); |
| return false; |
| }); |
| } else { |
| elem.text(msg); |
| } |
| }); |
| currentAssembly = data.asm || "[no output]"; |
| updateAsm(); |
| } |
| |
| function numberUsedLines(asm) { |
| var sourceLines = {}; |
| $.each(asm, function(_, x) { if (x.source) sourceLines[x.source - 1] = true; }); |
| var ordinal = 0; |
| $.each(sourceLines, function(k, _) { sourceLines[k] = ordinal++; }); |
| var asmLines = {}; |
| $.each(asm, function(index, x) { if (x.source) asmLines[index] = sourceLines[x.source - 1]; }); |
| return { source: sourceLines, asm: asmLines }; |
| } |
| |
| var lastUpdatedAsm = null; |
| function updateAsm(forceUpdate) { |
| if (!currentAssembly) return; |
| var hashedUpdate = JSON.stringify({ |
| asm: currentAssembly, |
| filter: filters |
| }); |
| if (!forceUpdate && lastUpdatedAsm == hashedUpdate) { return; } |
| lastUpdatedAsm = hashedUpdate; |
| |
| var asm = processAsm(currentAssembly, filters); |
| var asmText = $.map(asm, function(x){ return x.text; }).join("\n"); |
| var numberedLines = numberUsedLines(asm); |
| asmCodeMirror.setValue(asmText); |
| |
| clearBackground(cppEditor); |
| clearBackground(asmCodeMirror); |
| if (filters.colouriseAsm) { |
| $.each(numberedLines.source, function(line, ordinal) { |
| cppEditor.setLineClass(parseInt(line), null, "rainbow-" + (ordinal & 7)); |
| }); |
| $.each(numberedLines.asm, function(line, ordinal) { |
| asmCodeMirror.setLineClass(parseInt(line), null, "rainbow-" + (ordinal & 7)); |
| }); |
| } |
| } |
| |
| function onChange() { |
| if (ignoreChanges) return; // Ugly hack during startup. |
| if (pendingTimeout) clearTimeout(pendingTimeout); |
| pendingTimeout = setTimeout(function() { |
| var data = { |
| source: cppEditor.getValue(), |
| compiler: $('.compiler').val(), |
| options: $('.compiler_options').val(), |
| filters: filters |
| }; |
| window.localStorage['compiler'] = data.compiler; |
| window.localStorage['compilerOptions'] = data.options; |
| if (data == lastRequest) return; |
| lastRequest = data; |
| $.ajax({ |
| type: 'POST', |
| url: '/compile', |
| dataType: 'json', |
| data: data, |
| success: onCompileResponse}); |
| }, 750); |
| window.localStorage['code'] = cppEditor.getValue(); |
| updateAsm(); |
| $('a.permalink').attr('href', '#' + serialiseState()); |
| } |
| |
| function setSource(code) { |
| cppEditor.setValue(code); |
| } |
| |
| function getSource() { |
| return cppEditor.getValue(); |
| } |
| |
| function serialiseState() { |
| var state = { |
| version: 2, |
| source: cppEditor.getValue(), |
| compiler: domRoot.find('.compiler').val(), |
| options: domRoot.find('.compiler_options').val(), |
| filterAsm: filters |
| }; |
| return encodeURIComponent(JSON.stringify(state)); |
| } |
| |
| function deserialiseState(state) { |
| try { |
| state = $.parseJSON(decodeURIComponent(state)); |
| if (state.version == 1) { |
| state.filterAsm = {}; |
| } |
| else if (state.version != 2) return false; |
| } catch (ignored) { return false; } |
| cppEditor.setValue(state.source); |
| domRoot.find('.compiler').val(state.compiler); |
| domRoot.find('.compiler_options').val(state.options); |
| setFilterUi(state.filterAsm); |
| // Somewhat hackily persist compiler into local storage else when the ajax response comes in |
| // with the list of compilers it can splat over the deserialized version. |
| // The whole serialize/hash/localStorage code is a mess! TODO(mg): fix |
| window.localStorage['compiler'] = state.compiler; |
| updateAsm(true); // Force the update to reset colours after calling cppEditor.setValue |
| return true; |
| } |
| |
| function onCompilerChange() { |
| onChange(); |
| var compiler = compilersByExe[$('.compiler').val()]; |
| domRoot.find('.filter button.btn[value="intel"]').toggleClass("disabled", !compiler.supportedOpts["-masm"]); |
| } |
| |
| function setCompilers(compilers) { |
| domRoot.find('.compiler option').remove(); |
| compilersByExe = {}; |
| $.each(compilers, function(index, arg) { |
| compilersByExe[arg.exe] = arg; |
| $('.compiler').append($('<option value="' + arg.exe + '">' + arg.version + '</option>')); |
| }); |
| if (window.localStorage['compiler']) { |
| domRoot.find('.compiler').val(window.localStorage['compiler']); |
| } |
| onCompilerChange(); |
| } |
| |
| function setFilters(f) { |
| filters = f; |
| updateAsm(); |
| } |
| |
| return { |
| deserialiseState: deserialiseState, |
| setCompilers: setCompilers, |
| getSource: getSource, |
| setSource: setSource, |
| setFilters: setFilters |
| }; |
| } |