| // Copyright (c) 2012, Compiler Explorer Authors |
| // 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. |
| |
| 'use strict'; |
| var $ = require('jquery'); |
| var _ = require('underscore'); |
| var ga = require('../analytics').ga; |
| var colour = require('../colour'); |
| var Toggles = require('../widgets/toggles').Toggles; |
| var FontScale = require('../widgets/fontscale').FontScale; |
| var Promise = require('es6-promise').Promise; |
| var Components = require('../components'); |
| var LruCache = require('lru-cache'); |
| var options = require('../options').options; |
| var monaco = require('monaco-editor'); |
| var Alert = require('../alert').Alert; |
| var bigInt = require('big-integer'); |
| var LibsWidget = require('../widgets/libs-widget').LibsWidget; |
| var codeLensHandler = require('../codelens-handler'); |
| var monacoConfig = require('../monaco-config'); |
| var TimingWidget = require('../widgets/timing-info-widget'); |
| var CompilerPicker = require('../compiler-picker').CompilerPicker; |
| var Settings = require('../settings').Settings; |
| var utils = require('../utils'); |
| var LibUtils = require('../lib-utils'); |
| var getAssemblyDocumentation = require('../api/api').getAssemblyDocumentation; |
| var PaneRenaming = require('../widgets/pane-renaming').PaneRenaming; |
| |
| var OpcodeCache = new LruCache({ |
| max: 64 * 1024, |
| length: function (n) { |
| return JSON.stringify(n).length; |
| }, |
| }); |
| |
| function patchOldFilters(filters) { |
| if (filters === undefined) return undefined; |
| // Filters are of the form {filter: true|false¸ ...}. In older versions, we used |
| // to suppress the {filter:false} form. This means we can't distinguish between |
| // "filter not on" and "filter not present". In the latter case we want to default |
| // the filter. In the former case we want the filter off. Filters now don't suppress |
| // but there are plenty of permalinks out there with no filters set at all. Here |
| // we manually set any missing filters to 'false' to recover the old behaviour of |
| // "if it's not here, it's off". |
| _.each(['binary', 'labels', 'directives', 'commentOnly', 'trim', 'intel'], function (oldFilter) { |
| if (filters[oldFilter] === undefined) filters[oldFilter] = false; |
| }); |
| return filters; |
| } |
| |
| var languages = options.languages; |
| |
| // Disable max line count only for the constructor. Turns out, it needs to do quite a lot of things |
| // eslint-disable-next-line max-statements |
| function Compiler(hub, container, state) { |
| this.container = container; |
| this.hub = hub; |
| this.eventHub = hub.createEventHub(); |
| this.compilerService = hub.compilerService; |
| this.domRoot = container.getElement(); |
| this.domRoot.html($('#compiler').html()); |
| this.id = state.id || hub.nextCompilerId(); |
| this.sourceTreeId = state.tree ? state.tree : false; |
| if (this.sourceTreeId) { |
| this.sourceEditorId = false; |
| } else { |
| this.sourceEditorId = state.source || 1; |
| } |
| this.settings = Settings.getStoredSettings(); |
| this.originalCompilerId = state.compiler; |
| this.initLangAndCompiler(state); |
| this.infoByLang = {}; |
| this.deferCompiles = hub.deferred; |
| this.needsCompile = false; |
| this.deviceViewOpen = false; |
| this.options = state.options || options.compileOptions[this.currentLangId]; |
| this.source = ''; |
| this.assembly = []; |
| this.colours = []; |
| this.lastResult = {}; |
| this.lastTimeTaken = 0; |
| this.pendingRequestSentAt = 0; |
| this.pendingCMakeRequestSentAt = 0; |
| this.nextRequest = null; |
| this.nextCMakeRequest = null; |
| this.optViewOpen = false; |
| this.flagsViewOpen = state.flagsViewOpen || false; |
| this.cfgViewOpen = false; |
| this.wantOptInfo = state.wantOptInfo; |
| this.decorations = {}; |
| this.prevDecorations = []; |
| this.labelDefinitions = {}; |
| this.alertSystem = new Alert(); |
| this.alertSystem.prefixMessage = 'Compiler #' + this.id; |
| |
| this.awaitingInitialResults = false; |
| this.selection = state.selection; |
| |
| this.linkedFadeTimeoutId = -1; |
| this.toolsMenu = null; |
| |
| this.revealJumpStack = []; |
| |
| this.paneRenaming = new PaneRenaming(this, state); |
| |
| this.initButtons(state); |
| |
| var monacoDisassembly = 'asm'; |
| if (languages[this.currentLangId] && languages[this.currentLangId].monacoDisassembly) { |
| // TODO: If languages[this.currentLangId] is not valid, something went wrong. Find out what |
| monacoDisassembly = languages[this.currentLangId].monacoDisassembly; |
| } |
| |
| this.outputEditor = monaco.editor.create( |
| this.monacoPlaceholder[0], |
| monacoConfig.extendConfig( |
| { |
| readOnly: true, |
| language: monacoDisassembly, |
| glyphMargin: !options.embedded, |
| guides: false, |
| vimInUse: false, |
| }, |
| this.settings |
| ) |
| ); |
| |
| this.fontScale = new FontScale(this.domRoot, state, this.outputEditor); |
| this.compilerPicker = new CompilerPicker( |
| this.domRoot, |
| this.hub, |
| this.currentLangId, |
| this.compiler ? this.compiler.id : null, |
| _.bind(this.onCompilerChange, this) |
| ); |
| |
| this.initLibraries(state); |
| |
| this.initEditorActions(); |
| this.initEditorCommands(); |
| |
| this.initCallbacks(); |
| // Handle initial settings |
| this.onSettingsChange(this.settings); |
| this.sendCompiler(); |
| this.updateCompilerInfo(); |
| this.updateButtons(); |
| this.saveState(); |
| ga.proxy('send', { |
| hitType: 'event', |
| eventCategory: 'OpenViewPane', |
| eventAction: 'Compiler', |
| }); |
| |
| if (this.sourceTreeId) { |
| this.compile(); |
| } |
| } |
| |
| Compiler.prototype.getEditorIdBySourcefile = function (sourcefile) { |
| if (this.sourceTreeId) { |
| var tree = this.hub.getTreeById(this.sourceTreeId); |
| if (tree) { |
| return tree.multifileService.getEditorIdByFilename(sourcefile.file); |
| } |
| } else { |
| if (sourcefile !== null && (sourcefile.file === null || sourcefile.mainsource)) { |
| return this.sourceEditorId; |
| } |
| } |
| |
| return false; |
| }; |
| |
| Compiler.prototype.initLangAndCompiler = function (state) { |
| var langId = state.lang; |
| var compilerId = state.compiler; |
| var result = this.compilerService.processFromLangAndCompiler(langId, compilerId); |
| this.compiler = result.compiler; |
| this.currentLangId = result.langId; |
| this.updateLibraries(); |
| }; |
| |
| Compiler.prototype.close = function () { |
| codeLensHandler.unregister(this.id); |
| this.eventHub.unsubscribe(); |
| this.eventHub.emit('compilerClose', this.id, this.sourceTreeId); |
| this.outputEditor.dispose(); |
| }; |
| |
| // eslint-disable-next-line max-statements |
| Compiler.prototype.initPanerButtons = function () { |
| var outputConfig = _.bind(function () { |
| return Components.getOutput(this.id, this.sourceEditorId, this.sourceTreeId); |
| }, this); |
| |
| this.container.layoutManager.createDragSource(this.outputBtn, outputConfig); |
| this.outputBtn.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(outputConfig); |
| }, this) |
| ); |
| |
| var cloneComponent = _.bind(function () { |
| var currentState = this.currentState(); |
| // Delete the saved id to force a new one |
| delete currentState.id; |
| return { |
| type: 'component', |
| componentName: 'compiler', |
| componentState: currentState, |
| }; |
| }, this); |
| var createOptView = _.bind(function () { |
| return Components.getOptViewWith( |
| this.id, |
| this.source, |
| this.lastResult.optOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createFlagsView = _.bind(function () { |
| return Components.getFlagsViewWith(this.id, this.getCompilerName(), this.optionsField.val()); |
| }, this); |
| |
| if (this.flagsViewOpen) { |
| createFlagsView(); |
| } |
| |
| var createPpView = _.bind(function () { |
| return Components.getPpViewWith( |
| this.id, |
| this.source, |
| this.lastResult.ppOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createAstView = _.bind(function () { |
| return Components.getAstViewWith( |
| this.id, |
| this.source, |
| this.lastResult.astOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createIrView = _.bind(function () { |
| return Components.getIrViewWith( |
| this.id, |
| this.source, |
| this.lastResult.irOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createDeviceView = _.bind(function () { |
| return Components.getDeviceViewWith( |
| this.id, |
| this.source, |
| this.lastResult.devices, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createRustMirView = _.bind(function () { |
| return Components.getRustMirViewWith( |
| this.id, |
| this.source, |
| this.lastResult.rustMirOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createRustMacroExpView = _.bind(function () { |
| return Components.getRustMacroExpViewWith( |
| this.id, |
| this.source, |
| this.lastResult.rustMacroExpOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createRustHirView = _.bind(function () { |
| return Components.getRustHirViewWith( |
| this.id, |
| this.source, |
| this.lastResult.rustHirOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createHaskellCoreView = _.bind(function () { |
| return Components.getHaskellCoreViewWith( |
| this.id, |
| this.source, |
| this.lastResult.haskellCoreOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createHaskellStgView = _.bind(function () { |
| return Components.getHaskellStgViewWith( |
| this.id, |
| this.source, |
| this.lastResult.haskellStgOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createHaskellCmmView = _.bind(function () { |
| return Components.getHaskellCmmViewWith( |
| this.id, |
| this.source, |
| this.lastResult.haskellCmmOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createGccDumpView = _.bind(function () { |
| return Components.getGccDumpViewWith( |
| this.id, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId, |
| this.lastResult.gccDumpOutput |
| ); |
| }, this); |
| |
| var createGnatDebugTreeView = _.bind(function () { |
| return Components.getGnatDebugTreeViewWith( |
| this.id, |
| this.source, |
| this.lastResult.gnatDebugTreeOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createGnatDebugView = _.bind(function () { |
| return Components.getGnatDebugViewWith( |
| this.id, |
| this.source, |
| this.lastResult.gnatDebugOutput, |
| this.getCompilerName(), |
| this.sourceEditorId, |
| this.sourceTreeId |
| ); |
| }, this); |
| |
| var createCfgView = _.bind(function () { |
| return Components.getCfgViewWith(this.id, this.sourceEditorId, this.sourceTreeId); |
| }, this); |
| |
| var createExecutor = _.bind(function () { |
| var currentState = this.currentState(); |
| var editorId = currentState.source; |
| var treeId = currentState.tree; |
| var langId = currentState.lang; |
| var compilerId = currentState.compiler; |
| var libs = []; |
| _.each(this.libsWidget.getLibsInUse(), function (item) { |
| libs.push({ |
| name: item.libId, |
| ver: item.versionId, |
| }); |
| }); |
| return Components.getExecutorWith(editorId, langId, compilerId, libs, currentState.options, treeId); |
| }, this); |
| |
| var panerDropdown = this.domRoot.find('.pane-dropdown'); |
| var togglePannerAdder = function () { |
| panerDropdown.dropdown('toggle'); |
| }; |
| |
| this.container.layoutManager |
| .createDragSource(this.domRoot.find('.btn.add-compiler'), cloneComponent) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.domRoot.find('.btn.add-compiler').click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(cloneComponent); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.optButton, createOptView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.optButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createOptView); |
| }, this) |
| ); |
| |
| var popularArgumentsMenu = this.domRoot.find('div.populararguments div.dropdown-menu'); |
| this.container.layoutManager |
| .createDragSource(this.flagsButton, createFlagsView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.flagsButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createFlagsView); |
| }, this) |
| ); |
| |
| popularArgumentsMenu.append(this.flagsButton); |
| |
| this.container.layoutManager |
| .createDragSource(this.ppButton, createPpView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.ppButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createPpView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.astButton, createAstView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.astButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createAstView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.irButton, createIrView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.irButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createIrView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.deviceButton, createDeviceView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.deviceButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createDeviceView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.rustMirButton, createRustMirView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.rustMirButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createRustMirView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.haskellCoreButton, createHaskellCoreView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.haskellCoreButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createHaskellCoreView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.haskellStgButton, createHaskellStgView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.haskellStgButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createHaskellStgView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.haskellCmmButton, createHaskellCmmView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.haskellCmmButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createHaskellCmmView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.rustMacroExpButton, createRustMacroExpView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.rustMacroExpButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createRustMacroExpView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.rustHirButton, createRustHirView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.rustHirButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createRustHirView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.gccDumpButton, createGccDumpView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.gccDumpButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createGccDumpView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.gnatDebugTreeButton, createGnatDebugTreeView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.gnatDebugTreeButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createGnatDebugTreeView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.gnatDebugButton, createGnatDebugView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.gnatDebugButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createGnatDebugView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.cfgButton, createCfgView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.cfgButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createCfgView); |
| }, this) |
| ); |
| |
| this.container.layoutManager |
| .createDragSource(this.executorButton, createExecutor) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| this.executorButton.click( |
| _.bind(function () { |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createExecutor); |
| }, this) |
| ); |
| |
| this.initToolButtons(togglePannerAdder); |
| }; |
| |
| Compiler.prototype.undefer = function () { |
| this.deferCompiles = false; |
| if (this.needsCompile) { |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.resize = function () { |
| _.defer(function (self) { |
| var topBarHeight = utils.updateAndCalcTopBarHeight(self.domRoot, self.topBar, self.hideable); |
| var bottomBarHeight = self.bottomBar.outerHeight(true); |
| self.outputEditor.layout({ |
| width: self.domRoot.width(), |
| height: self.domRoot.height() - topBarHeight - bottomBarHeight, |
| }); |
| }, this); |
| }; |
| |
| // Returns a label name if it can be found in the given position, otherwise |
| // returns null. |
| Compiler.prototype.getLabelAtPosition = function (position) { |
| var asmLine = this.assembly[position.lineNumber - 1]; |
| // Outdated position.lineNumber can happen (Between compilations?) - Check for those and skip |
| if (asmLine) { |
| var column = position.column; |
| var labels = asmLine.labels || []; |
| |
| for (var i = 0; i < labels.length; ++i) { |
| if (column >= labels[i].range.startCol && column < labels[i].range.endCol) { |
| return labels[i]; |
| } |
| } |
| } |
| return null; |
| }; |
| |
| // Jumps to a label definition related to a label which was found in the |
| // given position and highlights the given range. If no label can be found in |
| // the given positon it do nothing. |
| Compiler.prototype.jumpToLabel = function (position) { |
| var label = this.getLabelAtPosition(position); |
| |
| if (!label) { |
| return; |
| } |
| |
| var labelDefLineNum = this.labelDefinitions[label.name]; |
| if (!labelDefLineNum) { |
| return; |
| } |
| |
| // Highlight the new range. |
| var endLineContent = this.outputEditor.getModel().getLineContent(labelDefLineNum); |
| |
| this.pushRevealJump(); |
| |
| this.outputEditor.setSelection( |
| new monaco.Selection(labelDefLineNum, 0, labelDefLineNum, endLineContent.length + 1) |
| ); |
| |
| // Jump to the given line. |
| this.outputEditor.revealLineInCenter(labelDefLineNum); |
| }; |
| |
| Compiler.prototype.pushRevealJump = function () { |
| this.revealJumpStack.push(this.outputEditor.saveViewState()); |
| this.revealJumpStackHasElementsCtxKey.set(true); |
| }; |
| |
| Compiler.prototype.popAndRevealJump = function () { |
| if (this.revealJumpStack.length > 0) { |
| this.outputEditor.restoreViewState(this.revealJumpStack.pop()); |
| this.revealJumpStackHasElementsCtxKey.set(this.revealJumpStack.length > 0); |
| } |
| }; |
| |
| Compiler.prototype.initEditorActions = function () { |
| this.isLabelCtxKey = this.outputEditor.createContextKey('isLabel', true); |
| this.revealJumpStackHasElementsCtxKey = this.outputEditor.createContextKey('hasRevealJumpStackElements', false); |
| this.isAsmKeywordCtxKey = this.outputEditor.createContextKey('isAsmKeyword', true); |
| |
| this.outputEditor.addAction({ |
| id: 'jumptolabel', |
| label: 'Jump to label', |
| keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter], |
| precondition: 'isLabel', |
| contextMenuGroupId: 'navigation', |
| contextMenuOrder: 1.5, |
| run: _.bind(function (ed) { |
| var position = ed.getPosition(); |
| if (position != null) { |
| this.jumpToLabel(position); |
| } |
| }, this), |
| }); |
| |
| // Hiding the 'Jump to label' context menu option if no label can be found |
| // in the clicked position. |
| var contextmenu = this.outputEditor.getContribution('editor.contrib.contextmenu'); |
| var realMethod = contextmenu._onContextMenu; |
| contextmenu._onContextMenu = _.bind(function (e) { |
| if (e && e.target && e.target.position) { |
| if (this.isLabelCtxKey) { |
| var label = this.getLabelAtPosition(e.target.position); |
| this.isLabelCtxKey.set(label !== null); |
| } |
| |
| if (this.isAsmKeywordCtxKey) { |
| if (!this.compiler.supportsAsmDocs) { |
| // No need to show the "Show asm documentation" if it's just going to fail. |
| // This is useful for things like xtensa which define an instructionSet but have no docs associated |
| this.isAsmKeywordCtxKey.set(false); |
| } else { |
| var currentWord = this.outputEditor.getModel().getWordAtPosition(e.target.position); |
| if (currentWord) { |
| currentWord.range = new monaco.Range( |
| e.target.position.lineNumber, |
| Math.max(currentWord.startColumn, 1), |
| e.target.position.lineNumber, |
| currentWord.endColumn |
| ); |
| if (currentWord.word) { |
| this.isAsmKeywordCtxKey.set(this.isWordAsmKeyword(currentWord)); |
| } |
| } |
| } |
| } |
| } |
| realMethod.apply(contextmenu, arguments); |
| }, this); |
| |
| this.outputEditor.addAction({ |
| id: 'returnfromreveal', |
| label: 'Return from reveal jump', |
| keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter], |
| contextMenuGroupId: 'navigation', |
| contextMenuOrder: 1.4, |
| precondition: 'hasRevealJumpStackElements', |
| run: _.bind(function () { |
| this.popAndRevealJump(); |
| }, this), |
| }); |
| |
| this.outputEditor.addAction({ |
| id: 'viewsource', |
| label: 'Scroll to source', |
| keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10], |
| keybindingContext: null, |
| contextMenuGroupId: 'navigation', |
| contextMenuOrder: 1.5, |
| run: _.bind(function (ed) { |
| var position = ed.getPosition(); |
| if (position != null) { |
| var desiredLine = position.lineNumber - 1; |
| var source = this.assembly[desiredLine].source; |
| if (source && source.line > 0) { |
| var editorId = this.getEditorIdBySourcefile(source); |
| if (editorId) { |
| // a null file means it was the user's source |
| this.eventHub.emit('editorLinkLine', editorId, source.line, -1, -1, true); |
| } |
| } |
| } |
| }, this), |
| }); |
| |
| this.outputEditor.addAction({ |
| id: 'viewasmdoc', |
| label: 'View assembly documentation', |
| keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F8], |
| keybindingContext: null, |
| precondition: 'isAsmKeyword', |
| contextMenuGroupId: 'help', |
| contextMenuOrder: 1.5, |
| run: _.bind(this.onAsmToolTip, this), |
| }); |
| |
| this.outputEditor.addAction({ |
| id: 'toggleColourisation', |
| label: 'Toggle colourisation', |
| keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.F1], |
| keybindingContext: null, |
| run: _.bind(function () { |
| this.eventHub.emit('modifySettings', { |
| colouriseAsm: !this.settings.colouriseAsm, |
| }); |
| }, this), |
| }); |
| }; |
| |
| Compiler.prototype.initEditorCommands = function () { |
| this.outputEditor.addAction({ |
| id: 'dumpAsm', |
| label: 'Developer: Dump asm', |
| run: _.bind(function () { |
| // eslint-disable-next-line no-console |
| console.log(this.assembly); |
| }, this), |
| }); |
| }; |
| |
| // Gets the filters that will actually be used (accounting for issues with binary |
| // mode etc). |
| Compiler.prototype.getEffectiveFilters = function () { |
| if (!this.compiler) return {}; |
| var filters = this.filters.get(); |
| if (filters.binary && !this.compiler.supportsBinary) { |
| delete filters.binary; |
| } |
| if (filters.execute && !this.compiler.supportsExecute) { |
| delete filters.execute; |
| } |
| if (filters.libraryCode && !this.compiler.supportsLibraryCodeFilter) { |
| delete filters.libraryCode; |
| } |
| _.each(this.compiler.disabledFilters, function (filter) { |
| if (filters[filter]) { |
| delete filters[filter]; |
| } |
| }); |
| return filters; |
| }; |
| |
| Compiler.prototype.findTools = function (content, tools) { |
| if (content.componentName === 'tool') { |
| if (content.componentState.compiler === this.id) { |
| tools.push({ |
| id: content.componentState.toolId, |
| args: content.componentState.args, |
| stdin: content.componentState.stdin, |
| }); |
| } |
| } else if (content.content) { |
| _.each( |
| content.content, |
| function (subcontent) { |
| tools = this.findTools(subcontent, tools); |
| }, |
| this |
| ); |
| } |
| |
| return tools; |
| }; |
| |
| Compiler.prototype.getActiveTools = function (newToolSettings) { |
| if (!this.compiler) return {}; |
| |
| var tools = []; |
| if (newToolSettings) { |
| tools.push({ |
| id: newToolSettings.toolId, |
| args: newToolSettings.args, |
| stdin: newToolSettings.stdin, |
| }); |
| } |
| |
| if (this.container.layoutManager.isInitialised) { |
| var config = this.container.layoutManager.toConfig(); |
| return this.findTools(config, tools); |
| } else { |
| return tools; |
| } |
| }; |
| |
| Compiler.prototype.isToolActive = function (activetools, toolId) { |
| return _.find(activetools, function (tool) { |
| return tool.id === toolId; |
| }); |
| }; |
| |
| Compiler.prototype.compile = function (bypassCache, newTools) { |
| if (this.deferCompiles) { |
| this.needsCompile = true; |
| return; |
| } |
| this.needsCompile = false; |
| this.compileInfoLabel.text(' - Compiling...'); |
| var options = { |
| userArguments: this.options, |
| compilerOptions: { |
| producePp: this.ppViewOpen ? this.ppOptions : false, |
| produceAst: this.astViewOpen, |
| produceGccDump: { |
| opened: this.gccDumpViewOpen, |
| pass: this.gccDumpPassSelected, |
| treeDump: this.treeDumpEnabled, |
| rtlDump: this.rtlDumpEnabled, |
| ipaDump: this.ipaDumpEnabled, |
| dumpFlags: this.dumpFlags, |
| }, |
| produceOptInfo: this.wantOptInfo, |
| produceCfg: this.cfgViewOpen, |
| produceGnatDebugTree: this.gnatDebugTreeViewOpen, |
| produceGnatDebug: this.gnatDebugViewOpen, |
| produceIr: this.irViewOpen, |
| produceDevice: this.deviceViewOpen, |
| produceRustMir: this.rustMirViewOpen, |
| produceRustMacroExp: this.rustMacroExpViewOpen, |
| produceRustHir: this.rustHirViewOpen, |
| produceHaskellCore: this.haskellCoreViewOpen, |
| produceHaskellStg: this.haskellStgViewOpen, |
| produceHaskellCmm: this.haskellCmmViewOpen, |
| }, |
| filters: this.getEffectiveFilters(), |
| tools: this.getActiveTools(newTools), |
| libraries: [], |
| }; |
| |
| _.each(this.libsWidget.getLibsInUse(), function (item) { |
| options.libraries.push({ |
| id: item.libId, |
| version: item.versionId, |
| }); |
| }); |
| |
| if (this.sourceTreeId) { |
| this.compileFromTree(options, bypassCache); |
| } else { |
| this.compileFromEditorSource(options, bypassCache); |
| } |
| }; |
| |
| Compiler.prototype.compileFromTree = function (options, bypassCache) { |
| var tree = this.hub.getTreeById(this.sourceTreeId); |
| if (!tree) { |
| this.sourceTreeId = false; |
| this.compileFromEditorSource(options, bypassCache); |
| return; |
| } |
| |
| var request = { |
| source: tree.multifileService.getMainSource(), |
| compiler: this.compiler ? this.compiler.id : '', |
| options: options, |
| lang: this.currentLangId, |
| files: tree.multifileService.getFiles(), |
| }; |
| |
| const fetches = []; |
| |
| fetches.push( |
| this.compilerService.expand(request.source).then(contents => { |
| request.source = contents; |
| }) |
| ); |
| |
| for (let file of request.files) { |
| fetches.push( |
| this.compilerService.expand(file.contents).then(contents => { |
| file.contents = contents; |
| }) |
| ); |
| } |
| |
| Promise.all(fetches).then(() => { |
| var treeState = tree.currentState(); |
| var cmakeProject = tree.multifileService.isACMakeProject(); |
| |
| if (bypassCache) request.bypassCache = true; |
| if (!this.compiler) { |
| this.onCompileResponse(request, errorResult('<Please select a compiler>'), false); |
| } else if (cmakeProject && request.source === '') { |
| this.onCompileResponse(request, errorResult('<Please supply a CMakeLists.txt>'), false); |
| } else { |
| if (cmakeProject) { |
| request.options.compilerOptions.cmakeArgs = treeState.cmakeArgs; |
| request.options.compilerOptions.customOutputFilename = treeState.customOutputFilename; |
| this.sendCMakeCompile(request); |
| } else { |
| this.sendCompile(request); |
| } |
| } |
| }); |
| }; |
| |
| Compiler.prototype.compileFromEditorSource = function (options, bypassCache) { |
| this.compilerService.expand(this.source).then( |
| _.bind(function (expanded) { |
| var request = { |
| source: expanded || '', |
| compiler: this.compiler ? this.compiler.id : '', |
| options: options, |
| lang: this.currentLangId, |
| files: [], |
| }; |
| if (bypassCache) request.bypassCache = true; |
| if (!this.compiler) { |
| this.onCompileResponse(request, errorResult('<Please select a compiler>'), false); |
| } else { |
| this.sendCompile(request); |
| } |
| }, this) |
| ); |
| }; |
| |
| Compiler.prototype.sendCMakeCompile = function (request) { |
| var onCompilerResponse = _.bind(this.onCMakeResponse, this); |
| |
| if (this.pendingCMakeRequestSentAt) { |
| // If we have a request pending, then just store this request to do once the |
| // previous request completes. |
| this.nextCMakeRequest = request; |
| return; |
| } |
| this.eventHub.emit('compiling', this.id, this.compiler); |
| // Display the spinner |
| this.handleCompilationStatus({code: 4}); |
| this.pendingCMakeRequestSentAt = Date.now(); |
| // After a short delay, give the user some indication that we're working on their |
| // compilation. |
| var progress = setTimeout( |
| _.bind(function () { |
| this.setAssembly({asm: fakeAsm('<Compiling...>')}, 0); |
| }, this), |
| 500 |
| ); |
| this.compilerService |
| .submitCMake(request) |
| .then(function (x) { |
| clearTimeout(progress); |
| onCompilerResponse(request, x.result, x.localCacheHit); |
| }) |
| .catch(function (x) { |
| clearTimeout(progress); |
| var message = 'Unknown error'; |
| if (_.isString(x)) { |
| message = x; |
| } else if (x) { |
| message = x.error || x.code || message; |
| } |
| onCompilerResponse(request, errorResult('<Compilation failed: ' + message + '>'), false); |
| }); |
| }; |
| |
| Compiler.prototype.sendCompile = function (request) { |
| var onCompilerResponse = _.bind(this.onCompileResponse, this); |
| |
| if (this.pendingRequestSentAt) { |
| // If we have a request pending, then just store this request to do once the |
| // previous request completes. |
| this.nextRequest = request; |
| return; |
| } |
| this.eventHub.emit('compiling', this.id, this.compiler); |
| // Display the spinner |
| this.handleCompilationStatus({code: 4}); |
| this.pendingRequestSentAt = Date.now(); |
| // After a short delay, give the user some indication that we're working on their |
| // compilation. |
| var progress = setTimeout( |
| _.bind(function () { |
| this.setAssembly({asm: fakeAsm('<Compiling...>')}, 0); |
| }, this), |
| 500 |
| ); |
| this.compilerService |
| .submit(request) |
| .then(function (x) { |
| clearTimeout(progress); |
| onCompilerResponse(request, x.result, x.localCacheHit); |
| }) |
| .catch(function (e) { |
| clearTimeout(progress); |
| var message = 'Unknown error'; |
| if (_.isString(e)) { |
| message = e; |
| } else if (e) { |
| message = e.error || e.code || e.message; |
| if (e.stack) { |
| // eslint-disable-next-line no-console |
| console.log(e); |
| } |
| } |
| onCompilerResponse(request, errorResult('<Compilation failed: ' + message + '>'), false); |
| }); |
| }; |
| |
| Compiler.prototype.setNormalMargin = function () { |
| this.outputEditor.updateOptions({ |
| lineNumbers: true, |
| lineNumbersMinChars: 1, |
| }); |
| }; |
| |
| Compiler.prototype.setBinaryMargin = function () { |
| this.outputEditor.updateOptions({ |
| lineNumbersMinChars: 6, |
| lineNumbers: _.bind(this.getBinaryForLine, this), |
| }); |
| }; |
| |
| Compiler.prototype.getBinaryForLine = function (line) { |
| var obj = this.assembly[line - 1]; |
| if (obj) { |
| return obj.address ? obj.address.toString(16) : ''; |
| } else { |
| return '???'; |
| } |
| }; |
| |
| Compiler.prototype.setAssembly = function (result, filteredCount) { |
| var asm = result.asm || fakeAsm('<No output>'); |
| this.assembly = asm; |
| if (!this.outputEditor || !this.outputEditor.getModel()) return; |
| var editorModel = this.outputEditor.getModel(); |
| var msg = '<No assembly generated>'; |
| if (asm.length) { |
| msg = _.pluck(asm, 'text').join('\n'); |
| } else if (filteredCount > 0) { |
| msg = '<No assembly to display (~' + filteredCount + (filteredCount === 1 ? ' line' : ' lines') + ' filtered)>'; |
| } |
| |
| if (asm.length === 1 && result.code !== 0 && (result.stderr || result.stdout)) { |
| msg += '\n\n# For more information see the output window'; |
| if (!this.isOutputOpened) { |
| msg += '\n# To open the output window, click or drag the "Output" icon at the bottom of this window'; |
| } |
| } |
| |
| editorModel.setValue(msg); |
| |
| if (!this.awaitingInitialResults) { |
| if (this.selection) { |
| this.outputEditor.setSelection(this.selection); |
| this.outputEditor.revealLinesInCenter(this.selection.startLineNumber, this.selection.endLineNumber); |
| } |
| this.awaitingInitialResults = true; |
| } else { |
| var visibleRanges = this.outputEditor.getVisibleRanges(); |
| var currentTopLine = visibleRanges.length > 0 ? visibleRanges[0].startLineNumber : 1; |
| this.outputEditor.revealLine(currentTopLine); |
| } |
| |
| this.decorations.labelUsages = []; |
| _.each( |
| this.assembly, |
| _.bind(function (obj, line) { |
| if (!obj.labels || !obj.labels.length) return; |
| |
| obj.labels.forEach(function (label) { |
| this.decorations.labelUsages.push({ |
| range: new monaco.Range(line + 1, label.range.startCol, line + 1, label.range.endCol), |
| options: { |
| inlineClassName: 'asm-label-link', |
| hoverMessage: [ |
| { |
| value: 'Ctrl + Left click to follow the label', |
| }, |
| ], |
| }, |
| }); |
| }, this); |
| }, this) |
| ); |
| this.updateDecorations(); |
| |
| var codeLenses = []; |
| if (this.getEffectiveFilters().binary || result.forceBinaryView) { |
| this.setBinaryMargin(); |
| _.each( |
| this.assembly, |
| _.bind(function (obj, line) { |
| if (obj.opcodes) { |
| var address = obj.address ? obj.address.toString(16) : ''; |
| codeLenses.push({ |
| range: { |
| startLineNumber: line + 1, |
| startColumn: 1, |
| endLineNumber: line + 2, |
| endColumn: 1, |
| }, |
| id: address, |
| command: { |
| title: obj.opcodes.join(' '), |
| }, |
| }); |
| } |
| }, this) |
| ); |
| } else { |
| this.setNormalMargin(); |
| } |
| |
| if (this.settings.enableCodeLens) { |
| codeLensHandler.registerLensesForCompiler(this.id, editorModel, codeLenses); |
| |
| var currentAsmLang = editorModel.getLanguageId(); |
| codeLensHandler.registerProviderForLanguage(currentAsmLang); |
| } else { |
| // Make sure the codelens is disabled |
| codeLensHandler.unregister(this.id); |
| } |
| }; |
| |
| function errorResult(text) { |
| return {asm: fakeAsm(text), code: -1, stdout: '', stderr: ''}; |
| } |
| |
| function fakeAsm(text) { |
| return [{text: text, source: null, fake: true}]; |
| } |
| |
| Compiler.prototype.doNextCompileRequest = function () { |
| if (this.nextRequest) { |
| var next = this.nextRequest; |
| this.nextRequest = null; |
| this.sendCompile(next); |
| } |
| }; |
| |
| Compiler.prototype.doNextCMakeRequest = function () { |
| if (this.nextCMakeRequest) { |
| var next = this.nextCMakeRequest; |
| this.nextCMakeRequest = null; |
| this.sendCMakeCompile(next); |
| } |
| }; |
| |
| Compiler.prototype.onCMakeResponse = function (request, result, cached) { |
| result.source = this.source; |
| this.lastResult = result; |
| var timeTaken = Math.max(0, Date.now() - this.pendingCMakeRequestSentAt); |
| this.lastTimeTaken = timeTaken; |
| var wasRealReply = this.pendingCMakeRequestSentAt > 0; |
| this.pendingCMakeRequestSentAt = 0; |
| |
| this.handleCompileRequestAndResult(request, result, cached, wasRealReply, timeTaken); |
| |
| this.doNextCMakeRequest(); |
| }; |
| |
| Compiler.prototype.handleCompileRequestAndResult = function (request, result, cached, wasRealReply, timeTaken) { |
| ga.proxy('send', { |
| hitType: 'event', |
| eventCategory: 'Compile', |
| eventAction: request.compiler, |
| eventLabel: request.options.userArguments, |
| eventValue: cached ? 1 : 0, |
| }); |
| ga.proxy('send', { |
| hitType: 'timing', |
| timingCategory: 'Compile', |
| timingVar: request.compiler, |
| timingValue: timeTaken, |
| }); |
| |
| // Delete trailing empty lines |
| if (Array.isArray(result.asm)) { |
| var indexToDiscard = _.findLastIndex(result.asm, function (line) { |
| return !_.isEmpty(line.text); |
| }); |
| result.asm.splice(indexToDiscard + 1, result.asm.length - indexToDiscard); |
| } |
| |
| this.labelDefinitions = result.labelDefinitions || {}; |
| if (result.asm) { |
| this.setAssembly(result, result.filteredCount || 0); |
| } else if (result.result && result.result.asm) { |
| this.setAssembly(result.result, result.result.filteredCount || 0); |
| } |
| |
| var stdout = result.stdout || []; |
| var stderr = result.stderr || []; |
| var failed = result.code ? result.code !== 0 : false; |
| |
| if (result.buildsteps) { |
| _.each(result.buildsteps, function (step) { |
| stdout = stdout.concat(step.stdout || []); |
| stderr = stderr.concat(step.stderr || []); |
| failed = failed | (step.code !== 0); |
| }); |
| } |
| |
| this.handleCompilationStatus(this.compilerService.calculateStatusIcon(result)); |
| this.outputTextCount.text(stdout.length); |
| this.outputErrorCount.text(stderr.length); |
| if (this.isOutputOpened || (stdout.length === 0 && stderr.length === 0)) { |
| this.outputBtn.prop('title', ''); |
| } else { |
| this.compilerService.handleOutputButtonTitle(this.outputBtn, result); |
| } |
| var infoLabelText = ''; |
| if (cached) { |
| infoLabelText = ' - cached'; |
| } else if (wasRealReply) { |
| infoLabelText = ' - ' + timeTaken + 'ms'; |
| } |
| |
| if (result.asmSize) { |
| infoLabelText += ' (' + result.asmSize + 'B)'; |
| } |
| |
| if (result.filteredCount && result.filteredCount > 0) { |
| infoLabelText += ' ~' + result.filteredCount + (result.filteredCount === 1 ? ' line' : ' lines') + ' filtered'; |
| } |
| |
| this.compileInfoLabel.text(infoLabelText); |
| |
| if (result.result) { |
| this.postCompilationResult(request, result.result); |
| } else { |
| this.postCompilationResult(request, result); |
| } |
| |
| this.eventHub.emit('compileResult', this.id, this.compiler, result, languages[this.currentLangId]); |
| }; |
| |
| Compiler.prototype.onCompileResponse = function (request, result, cached) { |
| // Save which source produced this change. It should probably be saved earlier though |
| result.source = this.source; |
| this.lastResult = result; |
| var timeTaken = Math.max(0, Date.now() - this.pendingRequestSentAt); |
| this.lastTimeTaken = timeTaken; |
| var wasRealReply = this.pendingRequestSentAt > 0; |
| this.pendingRequestSentAt = 0; |
| |
| this.handleCompileRequestAndResult(request, result, cached, wasRealReply, timeTaken); |
| |
| this.doNextCompileRequest(); |
| }; |
| |
| Compiler.prototype.postCompilationResult = function (request, result) { |
| if (result.popularArguments) { |
| this.handlePopularArgumentsResult(result.popularArguments); |
| } else { |
| this.compilerService.requestPopularArguments(this.compiler.id, request.options.userArguments).then( |
| _.bind(function (result) { |
| if (result && result.result) { |
| this.handlePopularArgumentsResult(result.result); |
| } |
| }, this) |
| ); |
| } |
| |
| this.updateButtons(); |
| |
| this.checkForUnwiseArguments(result.compilationOptions); |
| this.setCompilationOptionsPopover(result.compilationOptions ? result.compilationOptions.join(' ') : ''); |
| |
| this.checkForHints(result); |
| |
| if (result.bbcdiskimage) { |
| this.emulateBbcDisk(result.bbcdiskimage); |
| } |
| }; |
| |
| Compiler.prototype.emulateBbcDisk = function (bbcdiskimage) { |
| var dialog = $('#jsbeebemu'); |
| |
| this.alertSystem.notify( |
| 'Click <a target="_blank" id="emulink" style="cursor:pointer;" click="javascript:;">here</a> to emulate', |
| { |
| group: 'emulation', |
| collapseSimilar: true, |
| dismissTime: 10000, |
| onBeforeShow: function (elem) { |
| elem.find('#emulink').on('click', function () { |
| dialog.modal(); |
| |
| var emuwindow = dialog.find('#jsbeebemuframe')[0].contentWindow; |
| var tmstr = Date.now(); |
| emuwindow.location = |
| 'https://bbc.godbolt.org/?' + tmstr + '#embed&autoboot&disc1=b64data:' + bbcdiskimage; |
| }); |
| }, |
| } |
| ); |
| }; |
| |
| Compiler.prototype.onEditorChange = function (editor, source, langId, compilerId) { |
| if (this.sourceTreeId) { |
| var tree = this.hub.getTreeById(this.sourceTreeId); |
| if (tree) { |
| if (tree.multifileService.isEditorPartOfProject(editor)) { |
| if (this.settings.compileOnChange) { |
| this.compile(); |
| |
| return; |
| } |
| } |
| } |
| } |
| |
| if ( |
| editor === this.sourceEditorId && |
| langId === this.currentLangId && |
| (compilerId === undefined || compilerId === this.id) |
| ) { |
| this.source = source; |
| if (this.settings.compileOnChange) { |
| this.compile(); |
| } |
| } |
| }; |
| |
| Compiler.prototype.onCompilerFlagsChange = function (compilerId, compilerFlags) { |
| if (compilerId === this.id) { |
| this.onOptionsChange(compilerFlags); |
| } |
| }; |
| |
| Compiler.prototype.onToolOpened = function (compilerId, toolSettings) { |
| if (this.id === compilerId) { |
| var toolId = toolSettings.toolId; |
| |
| var buttons = this.toolsMenu.find('button'); |
| $(buttons).each( |
| _.bind(function (idx, button) { |
| var toolButton = $(button); |
| var toolName = toolButton.data('toolname'); |
| if (toolId === toolName) { |
| toolButton.prop('disabled', true); |
| } |
| }, this) |
| ); |
| |
| this.compile(false, toolSettings); |
| } |
| }; |
| |
| Compiler.prototype.onToolClosed = function (compilerId, toolSettings) { |
| if (this.id === compilerId) { |
| var toolId = toolSettings.toolId; |
| |
| var buttons = this.toolsMenu.find('button'); |
| $(buttons).each( |
| _.bind(function (idx, button) { |
| var toolButton = $(button); |
| var toolName = toolButton.data('toolname'); |
| if (toolId === toolName) { |
| toolButton.prop('disabled', !this.supportsTool(toolId)); |
| } |
| }, this) |
| ); |
| } |
| }; |
| |
| Compiler.prototype.onOutputOpened = function (compilerId) { |
| if (this.id === compilerId) { |
| this.isOutputOpened = true; |
| this.outputBtn.prop('disabled', true); |
| this.resendResult(); |
| } |
| }; |
| |
| Compiler.prototype.onOutputClosed = function (compilerId) { |
| if (this.id === compilerId) { |
| this.isOutputOpened = false; |
| this.outputBtn.prop('disabled', false); |
| } |
| }; |
| |
| Compiler.prototype.onOptViewClosed = function (id) { |
| if (this.id === id) { |
| this.wantOptInfo = false; |
| this.optViewOpen = false; |
| this.optButton.prop('disabled', this.optViewOpen); |
| } |
| }; |
| |
| Compiler.prototype.onFlagsViewClosed = function (id, compilerFlags) { |
| if (this.id === id) { |
| this.flagsViewOpen = false; |
| this.optionsField.val(compilerFlags); |
| this.optionsField.prop('disabled', this.flagsViewOpen); |
| this.optionsField.prop('placeholder', this.initialOptionsFieldPlacehoder); |
| this.flagsButton.prop('disabled', this.flagsViewOpen); |
| |
| this.compilerService.requestPopularArguments(this.compiler.id, compilerFlags).then( |
| _.bind(function (result) { |
| if (result && result.result) { |
| this.handlePopularArgumentsResult(result.result); |
| } |
| }, this) |
| ); |
| |
| this.saveState(); |
| } |
| }; |
| |
| Compiler.prototype.onToolSettingsChange = function (id) { |
| if (this.id === id) { |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onPpViewOpened = function (id) { |
| if (this.id === id) { |
| this.ppButton.prop('disabled', true); |
| this.ppViewOpen = true; |
| // the pp view will request compilation once it populates its options so this.compile() is not called here |
| } |
| }; |
| |
| Compiler.prototype.onPpViewClosed = function (id) { |
| if (this.id === id) { |
| this.ppButton.prop('disabled', false); |
| this.ppViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onPpViewOptionsUpdated = function (id, options, reqCompile) { |
| if (this.id === id) { |
| this.ppOptions = options; |
| if (reqCompile) { |
| this.compile(); |
| } |
| } |
| }; |
| |
| Compiler.prototype.onAstViewOpened = function (id) { |
| if (this.id === id) { |
| this.astButton.prop('disabled', true); |
| this.astViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onAstViewClosed = function (id) { |
| if (this.id === id) { |
| this.astButton.prop('disabled', false); |
| this.astViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onIrViewOpened = function (id) { |
| if (this.id === id) { |
| this.irButton.prop('disabled', true); |
| this.irViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onIrViewClosed = function (id) { |
| if (this.id === id) { |
| this.irButton.prop('disabled', false); |
| this.irViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onDeviceViewOpened = function (id) { |
| if (this.id === id) { |
| this.deviceButton.prop('disabled', true); |
| this.deviceViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onDeviceViewClosed = function (id) { |
| if (this.id === id) { |
| this.deviceButton.prop('disabled', false); |
| this.deviceViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onRustMirViewOpened = function (id) { |
| if (this.id === id) { |
| this.rustMirButton.prop('disabled', true); |
| this.rustMirViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onRustMirViewClosed = function (id) { |
| if (this.id === id) { |
| this.rustMirButton.prop('disabled', false); |
| this.rustMirViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onHaskellCoreViewOpened = function (id) { |
| if (this.id === id) { |
| this.haskellCoreButton.prop('disabled', true); |
| this.haskellCoreViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onHaskellCoreViewClosed = function (id) { |
| if (this.id === id) { |
| this.haskellCoreButton.prop('disabled', false); |
| this.haskellCoreViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onHaskellStgViewOpened = function (id) { |
| if (this.id === id) { |
| this.haskellStgButton.prop('disabled', true); |
| this.haskellStgViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onHaskellStgViewClosed = function (id) { |
| if (this.id === id) { |
| this.haskellStgButton.prop('disabled', false); |
| this.haskellStgViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onHaskellCmmViewOpened = function (id) { |
| if (this.id === id) { |
| this.haskellCmmButton.prop('disabled', true); |
| this.haskellCmmViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onHaskellCmmViewClosed = function (id) { |
| if (this.id === id) { |
| this.haskellCmmButton.prop('disabled', false); |
| this.haskellCmmViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onGnatDebugTreeViewOpened = function (id) { |
| if (this.id === id) { |
| this.gnatDebugTreeButton.prop('disabled', true); |
| this.gnatDebugTreeViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onGnatDebugTreeViewClosed = function (id) { |
| if (this.id === id) { |
| this.gnatDebugTreeButton.prop('disabled', false); |
| this.gnatDebugTreeViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onGnatDebugViewOpened = function (id) { |
| if (this.id === id) { |
| this.gnatDebugButton.prop('disabled', true); |
| this.gnatDebugViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onGnatDebugViewClosed = function (id) { |
| if (this.id === id) { |
| this.gnatDebugButton.prop('disabled', false); |
| this.gnatDebugViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onRustMacroExpViewOpened = function (id) { |
| if (this.id === id) { |
| this.rustMacroExpButton.prop('disabled', true); |
| this.rustMacroExpViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onRustMacroExpViewClosed = function (id) { |
| if (this.id === id) { |
| this.rustMacroExpButton.prop('disabled', false); |
| this.rustMacroExpViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onRustHirViewOpened = function (id) { |
| if (this.id === id) { |
| this.rustHirButton.prop('disabled', true); |
| this.rustHirViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onRustHirViewClosed = function (id) { |
| if (this.id === id) { |
| this.rustHirButton.prop('disabled', false); |
| this.rustHirViewOpen = false; |
| } |
| }; |
| |
| Compiler.prototype.onGccDumpUIInit = function (id) { |
| if (this.id === id) { |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onGccDumpFiltersChanged = function (id, filters, reqCompile) { |
| if (this.id === id) { |
| this.treeDumpEnabled = filters.treeDump !== false; |
| this.rtlDumpEnabled = filters.rtlDump !== false; |
| this.ipaDumpEnabled = filters.ipaDump !== false; |
| this.dumpFlags = { |
| address: filters.addressOption !== false, |
| slim: filters.slimOption !== false, |
| raw: filters.rawOption !== false, |
| details: filters.detailsOption !== false, |
| stats: filters.statsOption !== false, |
| blocks: filters.blocksOption !== false, |
| vops: filters.vopsOption !== false, |
| lineno: filters.linenoOption !== false, |
| uid: filters.uidOption !== false, |
| all: filters.allOption !== false, |
| }; |
| |
| if (reqCompile) { |
| this.compile(); |
| } |
| } |
| }; |
| |
| Compiler.prototype.onGccDumpPassSelected = function (id, passObject, reqCompile) { |
| if (this.id === id) { |
| this.gccDumpPassSelected = passObject; |
| |
| if (reqCompile && passObject !== null) { |
| this.compile(); |
| } |
| } |
| }; |
| |
| Compiler.prototype.onGccDumpViewOpened = function (id) { |
| if (this.id === id) { |
| this.gccDumpButton.prop('disabled', true); |
| this.gccDumpViewOpen = true; |
| } |
| }; |
| |
| Compiler.prototype.onGccDumpViewClosed = function (id) { |
| if (this.id === id) { |
| this.gccDumpButton.prop('disabled', !this.compiler.supportsGccDump); |
| this.gccDumpViewOpen = false; |
| |
| delete this.gccDumpPassSelected; |
| delete this.treeDumpEnabled; |
| delete this.rtlDumpEnabled; |
| delete this.ipaDumpEnabled; |
| delete this.dumpFlags; |
| } |
| }; |
| |
| Compiler.prototype.onOptViewOpened = function (id) { |
| if (this.id === id) { |
| this.optViewOpen = true; |
| this.wantOptInfo = true; |
| this.optButton.prop('disabled', this.optViewOpen); |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onFlagsViewOpened = function (id) { |
| if (this.id === id) { |
| this.flagsViewOpen = true; |
| this.handlePopularArgumentsResult(false); |
| this.optionsField.prop('disabled', this.flagsViewOpen); |
| this.optionsField.val(''); |
| this.optionsField.prop('placeholder', 'see detailed flags window'); |
| this.flagsButton.prop('disabled', this.flagsViewOpen); |
| this.compile(); |
| this.saveState(); |
| } |
| }; |
| |
| Compiler.prototype.onCfgViewOpened = function (id) { |
| if (this.id === id) { |
| this.cfgButton.prop('disabled', true); |
| this.cfgViewOpen = true; |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onCfgViewClosed = function (id) { |
| if (this.id === id) { |
| this.cfgViewOpen = false; |
| this.cfgButton.prop('disabled', this.getEffectiveFilters().binary); |
| } |
| }; |
| |
| Compiler.prototype.initFilterButtons = function () { |
| this.filterBinaryButton = this.domRoot.find("[data-bind='binary']"); |
| this.filterBinaryTitle = this.filterBinaryButton.prop('title'); |
| |
| this.filterExecuteButton = this.domRoot.find("[data-bind='execute']"); |
| this.filterExecuteTitle = this.filterExecuteButton.prop('title'); |
| |
| this.filterLabelsButton = this.domRoot.find("[data-bind='labels']"); |
| this.filterLabelsTitle = this.filterLabelsButton.prop('title'); |
| |
| this.filterDirectivesButton = this.domRoot.find("[data-bind='directives']"); |
| this.filterDirectivesTitle = this.filterDirectivesButton.prop('title'); |
| |
| this.filterLibraryCodeButton = this.domRoot.find("[data-bind='libraryCode']"); |
| this.filterLibraryCodeTitle = this.filterLibraryCodeButton.prop('title'); |
| |
| this.filterCommentsButton = this.domRoot.find("[data-bind='commentOnly']"); |
| this.filterCommentsTitle = this.filterCommentsButton.prop('title'); |
| |
| this.filterTrimButton = this.domRoot.find("[data-bind='trim']"); |
| this.filterTrimTitle = this.filterTrimButton.prop('title'); |
| |
| this.filterIntelButton = this.domRoot.find("[data-bind='intel']"); |
| this.filterIntelTitle = this.filterIntelButton.prop('title'); |
| |
| this.filterDemangleButton = this.domRoot.find("[data-bind='demangle']"); |
| this.filterDemangleTitle = this.filterDemangleButton.prop('title'); |
| |
| this.noBinaryFiltersButtons = this.domRoot.find('.nonbinary'); |
| }; |
| |
| Compiler.prototype.initButtons = function (state) { |
| this.filters = new Toggles(this.domRoot.find('.filters'), patchOldFilters(state.filters)); |
| |
| this.optButton = this.domRoot.find('.btn.view-optimization'); |
| this.flagsButton = this.domRoot.find('div.populararguments div.dropdown-menu button'); |
| this.ppButton = this.domRoot.find('.btn.view-pp'); |
| this.astButton = this.domRoot.find('.btn.view-ast'); |
| this.irButton = this.domRoot.find('.btn.view-ir'); |
| this.deviceButton = this.domRoot.find('.btn.view-device'); |
| this.gnatDebugTreeButton = this.domRoot.find('.btn.view-gnatdebugtree'); |
| this.gnatDebugButton = this.domRoot.find('.btn.view-gnatdebug'); |
| this.rustMirButton = this.domRoot.find('.btn.view-rustmir'); |
| this.rustMacroExpButton = this.domRoot.find('.btn.view-rustmacroexp'); |
| this.rustHirButton = this.domRoot.find('.btn.view-rusthir'); |
| this.haskellCoreButton = this.domRoot.find('.btn.view-haskellCore'); |
| this.haskellStgButton = this.domRoot.find('.btn.view-haskellStg'); |
| this.haskellCmmButton = this.domRoot.find('.btn.view-haskellCmm'); |
| this.gccDumpButton = this.domRoot.find('.btn.view-gccdump'); |
| this.cfgButton = this.domRoot.find('.btn.view-cfg'); |
| this.executorButton = this.domRoot.find('.create-executor'); |
| this.libsButton = this.domRoot.find('.btn.show-libs'); |
| |
| this.compileInfoLabel = this.domRoot.find('.compile-info'); |
| this.compileClearCache = this.domRoot.find('.clear-cache'); |
| |
| this.outputBtn = this.domRoot.find('.output-btn'); |
| this.outputTextCount = this.domRoot.find('span.text-count'); |
| this.outputErrorCount = this.domRoot.find('span.err-count'); |
| |
| this.optionsField = this.domRoot.find('.options'); |
| this.initialOptionsFieldPlacehoder = this.optionsField.prop('placeholder'); |
| this.prependOptions = this.domRoot.find('.prepend-options'); |
| this.fullCompilerName = this.domRoot.find('.full-compiler-name'); |
| this.fullTimingInfo = this.domRoot.find('.full-timing-info'); |
| this.setCompilationOptionsPopover(this.compiler ? this.compiler.options : null); |
| // Dismiss on any click that isn't either in the opening element, inside |
| // the popover or on any alert |
| $(document).on( |
| 'mouseup', |
| _.bind(function (e) { |
| var target = $(e.target); |
| if ( |
| !target.is(this.prependOptions) && |
| this.prependOptions.has(target).length === 0 && |
| target.closest('.popover').length === 0 |
| ) |
| this.prependOptions.popover('hide'); |
| |
| if ( |
| !target.is(this.fullCompilerName) && |
| this.fullCompilerName.has(target).length === 0 && |
| target.closest('.popover').length === 0 |
| ) |
| this.fullCompilerName.popover('hide'); |
| }, this) |
| ); |
| |
| this.initFilterButtons(state); |
| |
| this.filterExecuteButton.toggle(options.supportsExecute); |
| this.filterLibraryCodeButton.toggle(options.supportsLibraryCodeFilter); |
| |
| this.optionsField.val(this.options); |
| |
| this.shortCompilerName = this.domRoot.find('.short-compiler-name'); |
| this.compilerPicker = this.domRoot.find('.compiler-picker'); |
| this.setCompilerVersionPopover({version: '', fullVersion: ''}, ''); |
| |
| this.topBar = this.domRoot.find('.top-bar'); |
| this.bottomBar = this.domRoot.find('.bottom-bar'); |
| this.statusLabel = this.domRoot.find('.status-text'); |
| |
| this.hideable = this.domRoot.find('.hideable'); |
| this.statusIcon = this.domRoot.find('.status-icon'); |
| |
| this.monacoPlaceholder = this.domRoot.find('.monaco-placeholder'); |
| |
| this.initPanerButtons(); |
| }; |
| |
| Compiler.prototype.onLibsChanged = function () { |
| this.saveState(); |
| this.compile(); |
| }; |
| |
| Compiler.prototype.initLibraries = function (state) { |
| this.libsWidget = new LibsWidget( |
| this.currentLangId, |
| this.compiler, |
| this.libsButton, |
| state, |
| _.bind(this.onLibsChanged, this), |
| LibUtils.getSupportedLibraries( |
| this.compiler ? this.compiler.libsArr : [], |
| this.currentLangId, |
| this.compiler ? this.compiler.remote : null |
| ) |
| ); |
| }; |
| |
| Compiler.prototype.updateLibraries = function () { |
| if (this.libsWidget) { |
| var filteredLibraries = {}; |
| if (this.compiler) { |
| filteredLibraries = LibUtils.getSupportedLibraries( |
| this.compiler.libsArr, |
| this.currentLangId, |
| this.compiler ? this.compiler.remote : null |
| ); |
| } |
| |
| this.libsWidget.setNewLangId(this.currentLangId, this.compiler ? this.compiler.id : false, filteredLibraries); |
| } |
| }; |
| |
| Compiler.prototype.isSupportedTool = function (tool) { |
| if (this.sourceTreeId) { |
| return tool.tool.type === 'postcompilation'; |
| } else { |
| return true; |
| } |
| }; |
| |
| Compiler.prototype.supportsTool = function (toolId) { |
| if (!this.compiler) return; |
| |
| return _.find( |
| this.compiler.tools, |
| _.bind(function (tool) { |
| return tool.tool.id === toolId && this.isSupportedTool(tool); |
| }, this) |
| ); |
| }; |
| |
| Compiler.prototype.initToolButton = function (togglePannerAdder, button, toolId) { |
| var createToolView = _.bind(function () { |
| var args = ''; |
| var monacoStdin = false; |
| var langTools = options.tools[this.currentLangId]; |
| if (langTools && langTools[toolId] && langTools[toolId].tool) { |
| if (langTools[toolId].tool.args !== undefined) { |
| args = langTools[toolId].tool.args; |
| } |
| if (langTools[toolId].tool.monacoStdin !== undefined) { |
| monacoStdin = langTools[toolId].tool.monacoStdin; |
| } |
| } |
| return Components.getToolViewWith(this.id, this.sourceEditorId, toolId, args, monacoStdin, this.sourceTreeId); |
| }, this); |
| |
| this.container.layoutManager |
| .createDragSource(button, createToolView) |
| ._dragListener.on('dragStart', togglePannerAdder); |
| |
| button.click( |
| _.bind(function () { |
| button.prop('disabled', true); |
| var insertPoint = |
| this.hub.findParentRowOrColumn(this.container) || this.container.layoutManager.root.contentItems[0]; |
| insertPoint.addChild(createToolView); |
| }, this) |
| ); |
| }; |
| |
| Compiler.prototype.initToolButtons = function (togglePannerAdder) { |
| this.toolsMenu = this.domRoot.find('.toolsmenu'); |
| this.toolsMenu.empty(); |
| |
| if (!this.compiler) return; |
| |
| var addTool = _.bind(function (toolName, title) { |
| var btn = $("<button class='dropdown-item btn btn-light btn-sm'>"); |
| btn.addClass('view-' + toolName); |
| btn.data('toolname', toolName); |
| btn.append("<span class='dropdown-icon fas fa-cog'></span>" + title); |
| this.toolsMenu.append(btn); |
| |
| if (toolName !== 'none') { |
| this.initToolButton(togglePannerAdder, btn, toolName); |
| } |
| }, this); |
| |
| if (_.isEmpty(this.compiler.tools)) { |
| addTool('none', 'No tools available'); |
| } else { |
| _.each( |
| this.compiler.tools, |
| _.bind(function (tool) { |
| if (this.isSupportedTool(tool)) { |
| addTool(tool.tool.id, tool.tool.name); |
| } |
| }, this) |
| ); |
| } |
| }; |
| |
| Compiler.prototype.enableToolButtons = function () { |
| var activeTools = this.getActiveTools(); |
| |
| var buttons = this.toolsMenu.find('button'); |
| $(buttons).each( |
| _.bind(function (idx, button) { |
| var toolButton = $(button); |
| var toolName = toolButton.data('toolname'); |
| toolButton.prop('disabled', !(this.supportsTool(toolName) && !this.isToolActive(activeTools, toolName))); |
| }, this) |
| ); |
| }; |
| |
| // eslint-disable-next-line max-statements |
| Compiler.prototype.updateButtons = function () { |
| if (!this.compiler) return; |
| var filters = this.getEffectiveFilters(); |
| // We can support intel output if the compiler supports it, or if we're compiling |
| // to binary (as we can disassemble it however we like). |
| var formatFilterTitle = function (button, title) { |
| button.prop( |
| 'title', |
| '[' + |
| (button.hasClass('active') ? 'ON' : 'OFF') + |
| '] ' + |
| title + |
| (button.prop('disabled') ? ' [LOCKED]' : '') |
| ); |
| }; |
| var isIntelFilterDisabled = !this.compiler.supportsIntel && !filters.binary; |
| this.filterIntelButton.prop('disabled', isIntelFilterDisabled); |
| formatFilterTitle(this.filterIntelButton, this.filterIntelTitle); |
| // Disable binary support on compilers that don't work with it. |
| this.filterBinaryButton.prop('disabled', !this.compiler.supportsBinary); |
| formatFilterTitle(this.filterBinaryButton, this.filterBinaryTitle); |
| this.filterExecuteButton.prop('disabled', !this.compiler.supportsExecute); |
| formatFilterTitle(this.filterExecuteButton, this.filterExecuteTitle); |
| // Disable demangle for compilers where we can't access it |
| this.filterDemangleButton.prop('disabled', !this.compiler.supportsDemangle); |
| formatFilterTitle(this.filterDemangleButton, this.filterDemangleTitle); |
| // Disable any of the options which don't make sense in binary mode. |
| var noBinaryFiltersDisabled = !!filters.binary && !this.compiler.supportsFiltersInBinary; |
| this.noBinaryFiltersButtons.prop('disabled', noBinaryFiltersDisabled); |
| |
| this.filterLibraryCodeButton.prop('disabled', !this.compiler.supportsLibraryCodeFilter); |
| formatFilterTitle(this.filterLibraryCodeButton, this.filterLibraryCodeTitle); |
| |
| this.filterLabelsButton.prop('disabled', this.compiler.disabledFilters.indexOf('labels') !== -1); |
| formatFilterTitle(this.filterLabelsButton, this.filterLabelsTitle); |
| this.filterDirectivesButton.prop('disabled', this.compiler.disabledFilters.indexOf('directives') !== -1); |
| formatFilterTitle(this.filterDirectivesButton, this.filterDirectivesTitle); |
| this.filterCommentsButton.prop('disabled', this.compiler.disabledFilters.indexOf('commentOnly') !== -1); |
| formatFilterTitle(this.filterCommentsButton, this.filterCommentsTitle); |
| this.filterTrimButton.prop('disabled', this.compiler.disabledFilters.indexOf('trim') !== -1); |
| formatFilterTitle(this.filterTrimButton, this.filterTrimTitle); |
| |
| if (this.flagsButton) { |
| this.flagsButton.prop('disabled', this.flagsViewOpen); |
| } |
| this.optButton.prop('disabled', this.optViewOpen); |
| this.ppButton.prop('disabled', this.ppViewOpen); |
| this.astButton.prop('disabled', this.astViewOpen); |
| this.irButton.prop('disabled', this.irViewOpen); |
| this.deviceButton.prop('disabled', this.deviceViewOpen); |
| this.rustMirButton.prop('disabled', this.rustMirViewOpen); |
| this.haskellCoreButton.prop('disabled', this.haskellCoreViewOpen); |
| this.haskellStgButton.prop('disabled', this.haskellStgViewOpen); |
| this.haskellCmmButton.prop('disabled', this.haskellCmmViewOpen); |
| this.rustMacroExpButton.prop('disabled', this.rustMacroExpViewOpen); |
| this.rustHirButton.prop('disabled', this.rustHirViewOpen); |
| this.cfgButton.prop('disabled', this.cfgViewOpen); |
| this.gccDumpButton.prop('disabled', this.gccDumpViewOpen); |
| this.gnatDebugTreeButton.prop('disabled', this.gnatDebugTreeViewOpen); |
| this.gnatDebugButton.prop('disabled', this.gnatDebugViewOpen); |
| // The executorButton does not need to be changed here, because you can create however |
| // many executors as you want. |
| |
| this.optButton.toggle(!!this.compiler.supportsOptOutput); |
| this.ppButton.toggle(!!this.compiler.supportsPpView); |
| this.astButton.toggle(!!this.compiler.supportsAstView); |
| this.irButton.toggle(!!this.compiler.supportsIrView); |
| this.deviceButton.toggle(!!this.compiler.supportsDeviceAsmView); |
| this.rustMirButton.toggle(!!this.compiler.supportsRustMirView); |
| this.rustMacroExpButton.toggle(!!this.compiler.supportsRustMacroExpView); |
| this.rustHirButton.toggle(!!this.compiler.supportsRustHirView); |
| this.haskellCoreButton.toggle(!!this.compiler.supportsHaskellCoreView); |
| this.haskellStgButton.toggle(!!this.compiler.supportsHaskellStgView); |
| this.haskellCmmButton.toggle(!!this.compiler.supportsHaskellCmmView); |
| this.cfgButton.toggle(!!this.compiler.supportsCfg); |
| this.gccDumpButton.toggle(!!this.compiler.supportsGccDump); |
| this.gnatDebugTreeButton.toggle(!!this.compiler.supportsGnatDebugViews); |
| this.gnatDebugButton.toggle(!!this.compiler.supportsGnatDebugViews); |
| this.executorButton.toggle(!!this.compiler.supportsExecute); |
| |
| this.enableToolButtons(); |
| }; |
| |
| Compiler.prototype.handlePopularArgumentsResult = function (result) { |
| var popularArgumentsMenu = $(this.domRoot.find('div.populararguments div.dropdown-menu')); |
| |
| while (popularArgumentsMenu.children().length > 1) { |
| popularArgumentsMenu.children()[1].remove(); |
| } |
| |
| if (result && !this.flagsViewOpen) { |
| _.forEach( |
| result, |
| _.bind(function (arg, key) { |
| var argumentButton = $(document.createElement('button')); |
| argumentButton.addClass('dropdown-item btn btn-light btn-sm'); |
| argumentButton.attr('title', arg.description); |
| argumentButton.data('arg', key); |
| argumentButton.html( |
| "<div class='argmenuitem'>" + |
| "<span class='argtitle'>" + |
| _.escape(key) + |
| '</span>' + |
| "<span class='argdescription'>" + |
| arg.description + |
| '</span>' + |
| '</div>' |
| ); |
| |
| argumentButton.on( |
| 'click', |
| _.bind(function () { |
| var button = argumentButton; |
| var curOptions = this.optionsField.val(); |
| if (curOptions.length > 0) { |
| this.optionsField.val(curOptions + ' ' + button.data('arg')); |
| } else { |
| this.optionsField.val(button.data('arg')); |
| } |
| |
| this.optionsField.change(); |
| }, this) |
| ); |
| |
| popularArgumentsMenu.append(argumentButton); |
| }, this) |
| ); |
| } |
| }; |
| |
| Compiler.prototype.onFontScale = function () { |
| this.saveState(); |
| }; |
| |
| // Disable only for initListeners as there are more and more callbacks. |
| // eslint-disable-next-line max-statements |
| Compiler.prototype.initListeners = function () { |
| this.filters.on('change', _.bind(this.onFilterChange, this)); |
| this.fontScale.on('change', _.bind(this.onFontScale, this)); |
| this.eventHub.on( |
| 'broadcastFontScale', |
| _.bind(function (scale) { |
| this.fontScale.setScale(scale); |
| this.saveState(); |
| }, this) |
| ); |
| this.paneRenaming.on('renamePane', this.saveState.bind(this)); |
| |
| this.container.on('destroy', this.close, this); |
| this.container.on('resize', this.resize, this); |
| this.container.on('shown', this.resize, this); |
| this.container.on( |
| 'open', |
| function () { |
| this.eventHub.emit('compilerOpen', this.id, this.sourceEditorId, this.sourceTreeId); |
| }, |
| this |
| ); |
| this.eventHub.on('editorChange', this.onEditorChange, this); |
| this.eventHub.on('compilerFlagsChange', this.onCompilerFlagsChange, this); |
| this.eventHub.on('editorClose', this.onEditorClose, this); |
| this.eventHub.on('treeClose', this.onTreeClose, this); |
| this.eventHub.on('colours', this.onColours, this); |
| this.eventHub.on('coloursForCompiler', this.onColoursForCompiler, this); |
| this.eventHub.on('resendCompilation', this.onResendCompilation, this); |
| this.eventHub.on('findCompilers', this.sendCompiler, this); |
| this.eventHub.on('compilerSetDecorations', this.onCompilerSetDecorations, this); |
| this.eventHub.on('panesLinkLine', this.onPanesLinkLine, this); |
| this.eventHub.on('settingsChange', this.onSettingsChange, this); |
| this.eventHub.on('requestCompilation', this.onRequestCompilation, this); |
| |
| this.eventHub.on('toolSettingsChange', this.onToolSettingsChange, this); |
| this.eventHub.on('toolOpened', this.onToolOpened, this); |
| this.eventHub.on('toolClosed', this.onToolClosed, this); |
| |
| this.eventHub.on('optViewOpened', this.onOptViewOpened, this); |
| this.eventHub.on('optViewClosed', this.onOptViewClosed, this); |
| this.eventHub.on('flagsViewOpened', this.onFlagsViewOpened, this); |
| this.eventHub.on('flagsViewClosed', this.onFlagsViewClosed, this); |
| this.eventHub.on('ppViewOpened', this.onPpViewOpened, this); |
| this.eventHub.on('ppViewClosed', this.onPpViewClosed, this); |
| this.eventHub.on('ppViewOptionsUpdated', this.onPpViewOptionsUpdated, this); |
| this.eventHub.on('astViewOpened', this.onAstViewOpened, this); |
| this.eventHub.on('astViewClosed', this.onAstViewClosed, this); |
| this.eventHub.on('irViewOpened', this.onIrViewOpened, this); |
| this.eventHub.on('irViewClosed', this.onIrViewClosed, this); |
| this.eventHub.on('deviceViewOpened', this.onDeviceViewOpened, this); |
| this.eventHub.on('deviceViewClosed', this.onDeviceViewClosed, this); |
| this.eventHub.on('rustMirViewOpened', this.onRustMirViewOpened, this); |
| this.eventHub.on('rustMirViewClosed', this.onRustMirViewClosed, this); |
| this.eventHub.on('rustMacroExpViewOpened', this.onRustMacroExpViewOpened, this); |
| this.eventHub.on('rustMacroExpViewClosed', this.onRustMacroExpViewClosed, this); |
| this.eventHub.on('rustHirViewOpened', this.onRustHirViewOpened, this); |
| this.eventHub.on('rustHirViewClosed', this.onRustHirViewClosed, this); |
| this.eventHub.on('haskellCoreViewOpened', this.onHaskellCoreViewOpened, this); |
| this.eventHub.on('haskellCoreViewClosed', this.onHaskellCoreViewClosed, this); |
| this.eventHub.on('haskellStgViewOpened', this.onHaskellStgViewOpened, this); |
| this.eventHub.on('haskellStgViewClosed', this.onHaskellStgViewClosed, this); |
| this.eventHub.on('haskellCmmViewOpened', this.onHaskellCmmViewOpened, this); |
| this.eventHub.on('haskellCmmViewClosed', this.onHaskellCmmViewClosed, this); |
| this.eventHub.on('outputOpened', this.onOutputOpened, this); |
| this.eventHub.on('outputClosed', this.onOutputClosed, this); |
| |
| this.eventHub.on('gccDumpPassSelected', this.onGccDumpPassSelected, this); |
| this.eventHub.on('gccDumpFiltersChanged', this.onGccDumpFiltersChanged, this); |
| this.eventHub.on('gccDumpViewOpened', this.onGccDumpViewOpened, this); |
| this.eventHub.on('gccDumpViewClosed', this.onGccDumpViewClosed, this); |
| this.eventHub.on('gccDumpUIInit', this.onGccDumpUIInit, this); |
| |
| this.eventHub.on('gnatDebugTreeViewOpened', this.onGnatDebugTreeViewOpened, this); |
| this.eventHub.on('gnatDebugTreeViewClosed', this.onGnatDebugTreeViewClosed, this); |
| this.eventHub.on('gnatDebugViewOpened', this.onGnatDebugViewOpened, this); |
| this.eventHub.on('gnatDebugViewClosed', this.onGnatDebugViewClosed, this); |
| |
| this.eventHub.on('cfgViewOpened', this.onCfgViewOpened, this); |
| this.eventHub.on('cfgViewClosed', this.onCfgViewClosed, this); |
| this.eventHub.on('resize', this.resize, this); |
| this.eventHub.on( |
| 'requestFilters', |
| function (id) { |
| if (id === this.id) { |
| this.eventHub.emit('filtersChange', this.id, this.getEffectiveFilters()); |
| } |
| }, |
| this |
| ); |
| this.eventHub.on( |
| 'requestCompiler', |
| function (id) { |
| if (id === this.id) { |
| this.sendCompiler(); |
| } |
| }, |
| this |
| ); |
| this.eventHub.on('languageChange', this.onLanguageChange, this); |
| |
| this.fullTimingInfo.off('click').click( |
| _.bind(function () { |
| TimingWidget.displayCompilationTiming(this.lastResult, this.lastTimeTaken); |
| }, this) |
| ); |
| }; |
| |
| Compiler.prototype.initCallbacks = function () { |
| this.initListeners(); |
| |
| var optionsChange = _.debounce( |
| _.bind(function (e) { |
| this.onOptionsChange($(e.target).val()); |
| }, this), |
| 800 |
| ); |
| |
| this.optionsField.on('change', optionsChange).on('keyup', optionsChange); |
| |
| this.mouseMoveThrottledFunction = _.throttle(_.bind(this.onMouseMove, this), 50); |
| this.outputEditor.onMouseMove( |
| _.bind(function (e) { |
| this.mouseMoveThrottledFunction(e); |
| }, this) |
| ); |
| |
| this.cursorSelectionThrottledFunction = _.throttle(_.bind(this.onDidChangeCursorSelection, this), 500); |
| this.outputEditor.onDidChangeCursorSelection( |
| _.bind(function (e) { |
| this.cursorSelectionThrottledFunction(e); |
| }, this) |
| ); |
| |
| this.mouseUpThrottledFunction = _.throttle(_.bind(this.onMouseUp, this), 50); |
| this.outputEditor.onMouseUp( |
| _.bind(function (e) { |
| this.mouseUpThrottledFunction(e); |
| }, this) |
| ); |
| |
| this.compileClearCache.on( |
| 'click', |
| _.bind(function () { |
| this.compilerService.cache.reset(); |
| this.compile(true); |
| }, this) |
| ); |
| |
| // Dismiss the popover on escape. |
| $(document).on( |
| 'keyup.editable', |
| _.bind(function (e) { |
| if (e.which === 27) { |
| this.libsButton.popover('hide'); |
| } |
| }, this) |
| ); |
| |
| // Dismiss on any click that isn't either in the opening element, inside |
| // the popover or on any alert |
| $(document).on( |
| 'click', |
| _.bind(function (e) { |
| var elem = this.libsButton; |
| var target = $(e.target); |
| if (!target.is(elem) && elem.has(target).length === 0 && target.closest('.popover').length === 0) { |
| elem.popover('hide'); |
| } |
| }, this) |
| ); |
| |
| this.eventHub.on('initialised', this.undefer, this); |
| }; |
| |
| Compiler.prototype.onOptionsChange = function (options) { |
| if (this.options !== options) { |
| this.options = options; |
| this.saveState(); |
| this.compile(); |
| this.updateButtons(); |
| this.sendCompiler(); |
| } |
| }; |
| |
| function htmlEncode(rawStr) { |
| return rawStr.replace(/[\u00A0-\u9999<>&]/g, function (i) { |
| return '&#' + i.charCodeAt(0) + ';'; |
| }); |
| } |
| |
| Compiler.prototype.checkForHints = function (result) { |
| if (result.hints) { |
| result.hints.forEach(hint => { |
| this.alertSystem.notify(htmlEncode(hint), { |
| group: 'hints', |
| collapseSimilar: false, |
| }); |
| }); |
| } |
| }; |
| |
| Compiler.prototype.checkForUnwiseArguments = function (optionsArray) { |
| // Check if any options are in the unwiseOptions array and remember them |
| var unwiseOptions = _.intersection( |
| optionsArray, |
| _.filter(this.compiler.unwiseOptions, function (opt) { |
| return opt !== ''; |
| }) |
| ); |
| |
| var options = unwiseOptions.length === 1 ? 'Option ' : 'Options '; |
| var names = unwiseOptions.join(', '); |
| var are = unwiseOptions.length === 1 ? ' is ' : ' are '; |
| var msg = options + names + are + 'not recommended, as behaviour might change based on server hardware.'; |
| |
| if (_.contains(optionsArray, '-flto') && !this.filters.state.binary) { |
| this.alertSystem.notify('Option -flto is being used without Compile to Binary.', { |
| group: 'unwiseOption', |
| collapseSimilar: true, |
| }); |
| } |
| |
| if (unwiseOptions.length > 0) { |
| this.alertSystem.notify(msg, { |
| group: 'unwiseOption', |
| collapseSimilar: true, |
| }); |
| } |
| }; |
| |
| Compiler.prototype.updateCompilerInfo = function () { |
| this.updateCompilerName(); |
| if (this.compiler) { |
| if (this.compiler.notification) { |
| this.alertSystem.notify(this.compiler.notification, { |
| group: 'compilerwarning', |
| alertClass: 'notification-info', |
| dismissTime: 7000, |
| }); |
| } |
| this.prependOptions.data('content', this.compiler.options); |
| } |
| }; |
| |
| Compiler.prototype.updateCompilerUI = function () { |
| var panerDropdown = this.domRoot.find('.pane-dropdown'); |
| var togglePannerAdder = function () { |
| panerDropdown.dropdown('toggle'); |
| }; |
| this.initToolButtons(togglePannerAdder); |
| this.updateButtons(); |
| this.updateCompilerInfo(); |
| // Resize in case the new compiler name is too big |
| this.resize(); |
| }; |
| |
| Compiler.prototype.onCompilerChange = function (value) { |
| this.compiler = this.compilerService.findCompiler(this.currentLangId, value); |
| |
| this.deferCompiles = true; |
| this.needsCompile = true; |
| |
| this.updateLibraries(); |
| this.saveState(); |
| this.updateCompilerUI(); |
| |
| this.undefer(); |
| |
| this.sendCompiler(); |
| }; |
| |
| Compiler.prototype.sendCompiler = function () { |
| this.eventHub.emit('compiler', this.id, this.compiler, this.options, this.sourceEditorId, this.sourceTreeId); |
| }; |
| |
| Compiler.prototype.onEditorClose = function (editor) { |
| if (editor === this.sourceEditorId) { |
| // We can't immediately close as an outer loop somewhere in GoldenLayout is iterating over |
| // the hierarchy. We can't modify while it's being iterated over. |
| this.close(); |
| _.defer(function (self) { |
| self.container.close(); |
| }, this); |
| } |
| }; |
| |
| Compiler.prototype.onTreeClose = function (tree) { |
| if (tree === this.sourceTreeId) { |
| this.close(); |
| _.defer(function (self) { |
| self.container.close(); |
| }, this); |
| } |
| }; |
| |
| Compiler.prototype.onFilterChange = function () { |
| var filters = this.getEffectiveFilters(); |
| this.eventHub.emit('filtersChange', this.id, filters); |
| this.saveState(); |
| this.compile(); |
| this.updateButtons(); |
| }; |
| |
| Compiler.prototype.currentState = function () { |
| var state = { |
| id: this.id, |
| compiler: this.compiler ? this.compiler.id : '', |
| source: this.sourceEditorId, |
| tree: this.sourceTreeId, |
| options: this.options, |
| // NB must *not* be effective filters |
| filters: this.filters.get(), |
| wantOptInfo: this.wantOptInfo, |
| libs: this.libsWidget.get(), |
| lang: this.currentLangId, |
| selection: this.selection, |
| flagsViewOpen: this.flagsViewOpen, |
| }; |
| this.paneRenaming.addState(state); |
| this.fontScale.addState(state); |
| return state; |
| }; |
| |
| Compiler.prototype.saveState = function () { |
| this.container.setState(this.currentState()); |
| }; |
| |
| Compiler.prototype.onColours = function (editor, colours, scheme) { |
| var asmColours = {}; |
| _.each( |
| this.assembly, |
| _.bind(function (x, index) { |
| if (x.source && x.source.line > 0) { |
| var editorId = this.getEditorIdBySourcefile(x.source); |
| if (editorId === editor) { |
| if (!asmColours[editorId]) { |
| asmColours[editorId] = {}; |
| } |
| asmColours[editorId][index] = colours[x.source.line - 1]; |
| } |
| } |
| }, this) |
| ); |
| |
| _.each( |
| asmColours, |
| _.bind(function (col) { |
| this.colours = colour.applyColours(this.outputEditor, col, scheme, this.colours); |
| }, this) |
| ); |
| }; |
| |
| Compiler.prototype.onColoursForCompiler = function (compilerId, colours, scheme) { |
| if (this.id === compilerId) { |
| this.colours = colour.applyColours(this.outputEditor, colours, scheme, this.colours); |
| } |
| }; |
| |
| Compiler.prototype.getCompilerName = function () { |
| return this.compiler ? this.compiler.name : 'No compiler set'; |
| }; |
| |
| Compiler.prototype.getLanguageName = function () { |
| var lang = options.languages[this.currentLangId]; |
| return lang ? lang.name : '?'; |
| }; |
| |
| Compiler.prototype.getPaneName = function () { |
| var langName = this.getLanguageName(); |
| var compName = this.getCompilerName(); |
| if (this.sourceEditorId) { |
| return compName + ' (' + langName + ', Editor #' + this.sourceEditorId + ', Compiler #' + this.id + ')'; |
| } else if (this.sourceTreeId) { |
| return compName + ' (' + langName + ', Tree #' + this.sourceTreeId + ', Compiler #' + this.id + ')'; |
| } else { |
| return ''; |
| } |
| }; |
| |
| Compiler.prototype.updateTitle = function () { |
| var name = this.paneName ? this.paneName : this.getPaneName(); |
| this.container.setTitle(_.escape(name)); |
| }; |
| |
| Compiler.prototype.updateCompilerName = function () { |
| var compilerName = this.getCompilerName(); |
| var compilerVersion = this.compiler ? this.compiler.version : ''; |
| var compilerFullVersion = this.compiler && this.compiler.fullVersion ? this.compiler.fullVersion : compilerVersion; |
| var compilerNotification = this.compiler ? this.compiler.notification : ''; |
| this.shortCompilerName.text(compilerName); |
| this.setCompilerVersionPopover({version: compilerVersion, fullVersion: compilerFullVersion}, compilerNotification); |
| this.updateTitle(); |
| }; |
| |
| Compiler.prototype.resendResult = function () { |
| if (!$.isEmptyObject(this.lastResult)) { |
| this.eventHub.emit('compileResult', this.id, this.compiler, this.lastResult); |
| return true; |
| } |
| return false; |
| }; |
| |
| Compiler.prototype.onResendCompilation = function (id) { |
| if (id === this.id) { |
| this.resendResult(); |
| } |
| }; |
| |
| Compiler.prototype.updateDecorations = function () { |
| this.prevDecorations = this.outputEditor.deltaDecorations( |
| this.prevDecorations, |
| _.flatten(_.values(this.decorations)) |
| ); |
| }; |
| |
| Compiler.prototype.clearLinkedLines = function () { |
| this.decorations.linkedCode = []; |
| this.updateDecorations(); |
| }; |
| |
| Compiler.prototype.onPanesLinkLine = function (compilerId, lineNumber, colBegin, colEnd, revealLine, sender, editorId) { |
| if (Number(compilerId) === this.id) { |
| var lineNums = []; |
| var directlyLinkedLineNums = []; |
| var signalFromAnotherPane = sender !== this.getPaneName(); |
| _.each( |
| this.assembly, |
| _.bind(function (asmLine, i) { |
| if (asmLine.source && asmLine.source.line === lineNumber) { |
| var fileEditorId = this.getEditorIdBySourcefile(asmLine.source); |
| if (fileEditorId && editorId === fileEditorId) { |
| var line = i + 1; |
| lineNums.push(line); |
| var currentCol = asmLine.source.column; |
| if (signalFromAnotherPane && currentCol && colBegin <= currentCol && currentCol <= colEnd) { |
| directlyLinkedLineNums.push(line); |
| } |
| } |
| } |
| }, this) |
| ); |
| |
| if (revealLine && lineNums[0]) { |
| this.pushRevealJump(); |
| var tab = this.container.tab; |
| if (tab !== null) { |
| tab.header.setActiveContentItem(tab.contentItem); |
| } |
| this.outputEditor.revealLineInCenter(lineNums[0]); |
| } |
| |
| var lineClass = sender !== this.getPaneName() ? 'linked-code-decoration-line' : ''; |
| var linkedLinesDecoration = _.map(lineNums, function (line) { |
| return { |
| range: new monaco.Range(line, 1, line, 1), |
| options: { |
| isWholeLine: true, |
| linesDecorationsClassName: 'linked-code-decoration-margin', |
| className: lineClass, |
| }, |
| }; |
| }); |
| var directlyLinkedLinesDecoration = _.map(directlyLinkedLineNums, function (line) { |
| return { |
| range: new monaco.Range(line, 1, line, 1), |
| options: { |
| isWholeLine: true, |
| inlineClassName: 'linked-code-decoration-column', |
| }, |
| }; |
| }); |
| this.decorations.linkedCode = linkedLinesDecoration.concat(directlyLinkedLinesDecoration); |
| if (this.linkedFadeTimeoutId !== -1) { |
| clearTimeout(this.linkedFadeTimeoutId); |
| } |
| this.linkedFadeTimeoutId = setTimeout( |
| _.bind(function () { |
| this.clearLinkedLines(); |
| this.linkedFadeTimeoutId = -1; |
| }, this), |
| 5000 |
| ); |
| this.updateDecorations(); |
| } |
| }; |
| |
| Compiler.prototype.onCompilerSetDecorations = function (id, lineNums, revealLine) { |
| if (Number(id) === this.id) { |
| if (revealLine && lineNums[0]) { |
| this.pushRevealJump(); |
| this.outputEditor.revealLineInCenter(lineNums[0]); |
| } |
| this.decorations.linkedCode = _.map(lineNums, function (line) { |
| return { |
| range: new monaco.Range(line, 1, line, 1), |
| options: { |
| isWholeLine: true, |
| linesDecorationsClassName: 'linked-code-decoration-margin', |
| inlineClassName: 'linked-code-decoration-inline', |
| }, |
| }; |
| }); |
| this.updateDecorations(); |
| } |
| }; |
| |
| Compiler.prototype.setCompilationOptionsPopover = function (content) { |
| this.prependOptions.popover('dispose'); |
| this.prependOptions.popover({ |
| content: content || 'No options in use', |
| template: |
| '<div class="popover' + |
| (content ? ' compiler-options-popover' : '') + |
| '" role="tooltip"><div class="arrow"></div>' + |
| '<h3 class="popover-header"></h3><div class="popover-body"></div></div>', |
| }); |
| }; |
| |
| Compiler.prototype.setCompilerVersionPopover = function (version, notification) { |
| this.fullCompilerName.popover('dispose'); |
| // `notification` contains HTML from a config file, so is 'safe'. |
| // `version` comes from compiler output, so isn't, and is escaped. |
| var bodyContent = $('<div>'); |
| var versionContent = $('<div>').html(_.escape(version.version)); |
| bodyContent.append(versionContent); |
| if (version.fullVersion) { |
| var hiddenSection = $('<div>'); |
| var lines = _.map(version.fullVersion.split('\n'), function (line) { |
| return _.escape(line); |
| }).join('<br/>'); |
| var hiddenVersionText = $('<div>').html(lines).hide(); |
| var clickToExpandContent = $('<a>') |
| .attr('href', 'javascript:;') |
| .text('Toggle full version output') |
| .on( |
| 'click', |
| _.bind(function () { |
| versionContent.toggle(); |
| hiddenVersionText.toggle(); |
| this.fullCompilerName.popover('update'); |
| }, this) |
| ); |
| hiddenSection.append(hiddenVersionText).append(clickToExpandContent); |
| bodyContent.append(hiddenSection); |
| } |
| this.fullCompilerName.popover({ |
| html: true, |
| title: notification |
| ? $.parseHTML('<span>Compiler Version: ' + notification + '</span>')[0] |
| : 'Full compiler version', |
| content: bodyContent, |
| template: |
| '<div class="popover' + |
| (version ? ' compiler-options-popover' : '') + |
| '" role="tooltip">' + |
| '<div class="arrow"></div>' + |
| '<h3 class="popover-header"></h3><div class="popover-body"></div>' + |
| '</div>', |
| }); |
| }; |
| |
| Compiler.prototype.onRequestCompilation = function (editorId, treeId) { |
| if (editorId === this.sourceEditorId || (treeId && treeId === this.sourceTreeId)) { |
| this.compile(); |
| } |
| }; |
| |
| Compiler.prototype.onSettingsChange = function (newSettings) { |
| var before = this.settings; |
| this.settings = _.clone(newSettings); |
| if (!before.lastHoverShowSource && this.settings.hoverShowSource) { |
| this.onCompilerSetDecorations(this.id, []); |
| } |
| this.outputEditor.updateOptions({ |
| contextmenu: this.settings.useCustomContextMenu, |
| minimap: { |
| enabled: this.settings.showMinimap && !options.embedded, |
| }, |
| fontFamily: this.settings.editorsFFont, |
| codeLensFontFamily: this.settings.editorsFFont, |
| fontLigatures: this.settings.editorsFLigatures, |
| }); |
| }; |
| |
| var hexLike = /^(#?[$]|0x)([0-9a-fA-F]+)$/; |
| var hexLike2 = /^(#?)([0-9a-fA-F]+)H$/; |
| var decimalLike = /^(#?)(-?[0-9]+)$/; |
| |
| function parseNumericValue(value) { |
| var hexMatch = hexLike.exec(value) || hexLike2.exec(value); |
| if (hexMatch) return bigInt(hexMatch[2], 16); |
| |
| var decMatch = decimalLike.exec(value); |
| if (decMatch) return bigInt(decMatch[2]); |
| |
| return null; |
| } |
| |
| function getNumericToolTip(value) { |
| var numericValue = parseNumericValue(value); |
| if (numericValue === null) return null; |
| |
| // Decimal representation. |
| var result = numericValue.toString(10); |
| |
| // Hexadecimal representation. |
| if (numericValue.isNegative()) { |
| var masked = bigInt('ffffffffffffffff', 16).and(numericValue); |
| result += ' = 0x' + masked.toString(16).toUpperCase(); |
| } else { |
| result += ' = 0x' + numericValue.toString(16).toUpperCase(); |
| } |
| |
| // Printable ASCII character. |
| if (numericValue.greaterOrEquals(0x20) && numericValue.lesserOrEquals(0x7e)) { |
| var char = String.fromCharCode(numericValue.valueOf()); |
| result += " = '" + char + "'"; |
| } |
| |
| return result; |
| } |
| |
| function getAsmInfo(opcode, instructionSet) { |
| var cacheName = 'asm/' + (instructionSet ? instructionSet + '/' : '') + opcode; |
| var cached = OpcodeCache.get(cacheName); |
| if (cached) { |
| if (cached.found) { |
| return Promise.resolve(cached.data); |
| } |
| return Promise.reject(cached.data); |
| } |
| return new Promise(function (resolve, reject) { |
| getAssemblyDocumentation({opcode: opcode, instructionSet: instructionSet}) |
| .then(function (response) { |
| response.json().then(function (body) { |
| if (response.status === 200) { |
| OpcodeCache.set(cacheName, {found: true, data: body}); |
| resolve(body); |
| } else { |
| OpcodeCache.set(cacheName, {found: false, data: body.error}); |
| reject(body.error); |
| } |
| }); |
| }) |
| .catch(function (error) { |
| reject('Fetch error: ' + error); |
| }); |
| }); |
| } |
| |
| Compiler.prototype.onDidChangeCursorSelection = function (e) { |
| if (this.awaitingInitialResults) { |
| this.selection = e.selection; |
| this.saveState(); |
| } |
| }; |
| |
| Compiler.prototype.onMouseUp = function (e) { |
| if (e === null || e.target === null || e.target.position === null) return; |
| |
| if (e.event.ctrlKey && e.event.leftButton) { |
| this.jumpToLabel(e.target.position); |
| } |
| }; |
| |
| Compiler.prototype.onMouseMove = function (e) { |
| if (e === null || e.target === null || e.target.position === null) return; |
| var hoverShowSource = this.settings.hoverShowSource === true; |
| if (this.assembly) { |
| var hoverAsm = this.assembly[e.target.position.lineNumber - 1]; |
| if (hoverShowSource && hoverAsm) { |
| this.clearLinkedLines(); |
| // We check that we actually have something to show at this point! |
| var sourceLine = -1; |
| var sourceColBegin = -1; |
| var sourceColEnd = -1; |
| if (hoverAsm.source) { |
| sourceLine = hoverAsm.source.line; |
| if (hoverAsm.source.column) { |
| sourceColBegin = hoverAsm.source.column; |
| sourceColEnd = sourceColBegin; |
| } |
| |
| var editorId = this.getEditorIdBySourcefile(hoverAsm.source); |
| if (editorId) { |
| this.eventHub.emit('editorLinkLine', editorId, sourceLine, sourceColBegin, sourceColEnd, false); |
| |
| this.eventHub.emit( |
| 'panesLinkLine', |
| this.id, |
| sourceLine, |
| sourceColBegin, |
| sourceColEnd, |
| false, |
| this.getPaneName(), |
| editorId |
| ); |
| } |
| } |
| } |
| } |
| var currentWord = this.outputEditor.getModel().getWordAtPosition(e.target.position); |
| if (currentWord && currentWord.word) { |
| var word = currentWord.word; |
| var startColumn = currentWord.startColumn; |
| // Avoid throwing an exception if somehow (How?) we have a non existent lineNumber. |
| // c.f. https://sentry.io/matt-godbolt/compiler-explorer/issues/285270358/ |
| if (e.target.position.lineNumber <= this.outputEditor.getModel().getLineCount()) { |
| // Hacky workaround to check for negative numbers. |
| // c.f. https://github.com/compiler-explorer/compiler-explorer/issues/434 |
| var lineContent = this.outputEditor.getModel().getLineContent(e.target.position.lineNumber); |
| if (lineContent[currentWord.startColumn - 2] === '-') { |
| word = '-' + word; |
| startColumn -= 1; |
| } |
| } |
| currentWord.range = new monaco.Range( |
| e.target.position.lineNumber, |
| Math.max(startColumn, 1), |
| e.target.position.lineNumber, |
| currentWord.endColumn |
| ); |
| var numericToolTip = getNumericToolTip(word); |
| if (numericToolTip) { |
| this.decorations.numericToolTip = { |
| range: currentWord.range, |
| options: { |
| isWholeLine: false, |
| hoverMessage: [ |
| { |
| // We use double `` as numericToolTip may include a single ` character. |
| value: '``' + numericToolTip + '``', |
| }, |
| ], |
| }, |
| }; |
| this.updateDecorations(); |
| } |
| var hoverShowAsmDoc = this.settings.hoverShowAsmDoc === true; |
| if (hoverShowAsmDoc && this.compiler && this.compiler.supportsAsmDocs && this.isWordAsmKeyword(currentWord)) { |
| getAsmInfo(currentWord.word, this.compiler.instructionSet).then( |
| _.bind(function (response) { |
| if (!response) return; |
| this.decorations.asmToolTip = { |
| range: currentWord.range, |
| options: { |
| isWholeLine: false, |
| hoverMessage: [ |
| { |
| value: response.tooltip + '\n\nMore information available in the context menu.', |
| isTrusted: true, |
| }, |
| ], |
| }, |
| }; |
| this.updateDecorations(); |
| }, this) |
| ); |
| } |
| } |
| }; |
| |
| Compiler.prototype.getLineTokens = function (line) { |
| var model = this.outputEditor.getModel(); |
| if (!model || line > model.getLineCount()) return []; |
| var flavour = model.getLanguageId(); |
| var tokens = monaco.editor.tokenize(model.getLineContent(line), flavour); |
| return tokens.length > 0 ? tokens[0] : []; |
| }; |
| |
| Compiler.prototype.isWordAsmKeyword = function (word) { |
| return _.some(this.getLineTokens(word.range.startLineNumber), function (t) { |
| return t.offset + 1 === word.startColumn && t.type === 'keyword.asm'; |
| }); |
| }; |
| |
| Compiler.prototype.onAsmToolTip = function (ed) { |
| ga.proxy('send', { |
| hitType: 'event', |
| eventCategory: 'OpenModalPane', |
| eventAction: 'AsmDocs', |
| }); |
| var pos = ed.getPosition(); |
| if (!pos || !ed.getModel()) return; |
| var word = ed.getModel().getWordAtPosition(pos); |
| if (!word || !word.word) return; |
| var opcode = word.word.toUpperCase(); |
| |
| function newGitHubIssueUrl() { |
| return ( |
| 'https://github.com/compiler-explorer/compiler-explorer/issues/new?title=' + |
| encodeURIComponent('[BUG] Problem with ' + opcode + ' opcode') |
| ); |
| } |
| |
| function appendInfo(url) { |
| return ( |
| '<br><br>For more information, visit <a href="' + |
| url + |
| '" target="_blank" rel="noopener noreferrer">the ' + |
| opcode + |
| ' documentation <sup><small class="fas fa-external-link-alt opens-new-window"' + |
| ' title="Opens in a new window"></small></sup></a>.' + |
| '<br>If the documentation for this opcode is wrong or broken in some way, ' + |
| 'please feel free to <a href="' + |
| newGitHubIssueUrl() + |
| '" target="_blank" rel="noopener noreferrer">' + |
| 'open an issue on GitHub <sup><small class="fas fa-external-link-alt opens-new-window" ' + |
| 'title="Opens in a new window"></small></sup></a>.' |
| ); |
| } |
| |
| getAsmInfo(word.word, this.compiler.instructionSet).then( |
| _.bind(function (asmHelp) { |
| if (asmHelp) { |
| this.alertSystem.alert(opcode + ' help', asmHelp.html + appendInfo(asmHelp.url), function () { |
| ed.focus(); |
| ed.setPosition(pos); |
| }); |
| } else { |
| this.alertSystem.notify('This token was not found in the documentation. Sorry!', { |
| group: 'notokenindocs', |
| alertClass: 'notification-error', |
| dismissTime: 5000, |
| }); |
| } |
| }, this), |
| _.bind(function (rejection) { |
| this.alertSystem.notify( |
| 'There was an error fetching the documentation for this opcode (' + rejection + ').', |
| { |
| group: 'notokenindocs', |
| alertClass: 'notification-error', |
| dismissTime: 5000, |
| } |
| ); |
| }, this) |
| ); |
| }; |
| |
| Compiler.prototype.handleCompilationStatus = function (status) { |
| this.compilerService.handleCompilationStatus(this.statusLabel, this.statusIcon, status); |
| }; |
| |
| Compiler.prototype.onLanguageChange = function (editorId, newLangId, treeId) { |
| if ( |
| (this.sourceEditorId && this.sourceEditorId === editorId) || |
| (this.sourceTreeId && this.sourceTreeId === treeId) |
| ) { |
| var oldLangId = this.currentLangId; |
| this.currentLangId = newLangId; |
| // Store the current selected stuff to come back to it later in the same session (Not state stored!) |
| this.infoByLang[oldLangId] = { |
| compiler: this.compiler && this.compiler.id ? this.compiler.id : options.defaultCompiler[oldLangId], |
| options: this.options, |
| }; |
| var info = this.infoByLang[this.currentLangId] || {}; |
| this.deferCompiles = true; |
| this.initLangAndCompiler({lang: newLangId, compiler: info.compiler}); |
| this.updateCompilersSelector(info); |
| this.saveState(); |
| this.updateCompilerUI(); |
| this.setAssembly(fakeAsm('')); |
| // this is a workaround to delay compilation further until the Editor sends a compile request |
| this.needsCompile = false; |
| |
| this.undefer(); |
| this.sendCompiler(); |
| } |
| }; |
| |
| Compiler.prototype.getCurrentLangCompilers = function () { |
| return this.compilerService.getCompilersForLang(this.currentLangId); |
| }; |
| |
| Compiler.prototype.updateCompilersSelector = function (info) { |
| this.compilerPicker.update(this.currentLangId, this.compiler ? this.compiler.id : null); |
| this.options = info.options || ''; |
| this.optionsField.val(this.options); |
| }; |
| |
| module.exports = { |
| Compiler: Compiler, |
| }; |