blob: 2bb0e04584029589e7d95096a55d50166fd3ebb9 [file] [log] [blame]
/*
* Copyright (C) 2015 Apple Inc. All rights reserved.
* Copyright (C) 2015 Saam Barati <saambarati1@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
*/
WI.BasicBlockAnnotator = class BasicBlockAnnotator extends WI.Annotator
{
constructor(sourceCodeTextEditor, script)
{
super(sourceCodeTextEditor);
this._script = script;
this._basicBlockMarkers = new Map; // Only contains unexecuted basic blocks.
}
// Protected
clearAnnotations()
{
for (var key of this._basicBlockMarkers.keys())
this._clearRangeForBasicBlockMarker(key);
}
insertAnnotations()
{
if (!this.isActive())
return;
this._script.requestContent().then(this._annotateBasicBlockExecutionRanges.bind(this));
}
// Private
_calculateBasicBlockPositions(basicBlocks, content)
{
if (!basicBlocks || !basicBlocks.length)
return;
let lineEndings = [];
let lineEndingLengths = [];
let pattern = /\r\n?|\n/g;
let match = pattern.exec(content);
while (match) {
lineEndings.push(match.index);
lineEndingLengths.push(match[0].length);
match = pattern.exec(content);
}
function offsetToPosition(offset) {
let lineNumber = lineEndings.upperBound(offset - 1);
if (lineNumber) {
let previousLine = lineNumber - 1;
var columnNumber = offset - lineEndings[previousLine] - lineEndingLengths[previousLine];
} else
var columnNumber = offset;
return new WI.SourceCodePosition(lineNumber, columnNumber);
}
for (let block of basicBlocks) {
block.startPosition = offsetToPosition(block.startOffset);
block.endPosition = offsetToPosition(block.endOffset);
}
}
_annotateBasicBlockExecutionRanges()
{
let content = this._script.content;
console.assert(content, "Missing script content for basic block annotations.");
if (!content)
return;
var sourceID = this._script.id;
var startTime = Date.now();
this._script.target.RuntimeAgent.getBasicBlocks(sourceID, function(error, basicBlocks) {
if (error) {
console.error("Error in getting basic block locations: " + error);
return;
}
if (!this.isActive())
return;
this._calculateBasicBlockPositions(basicBlocks, content);
let {startPosition, endPosition} = this.sourceCodeTextEditor.visibleRangePositions();
basicBlocks = basicBlocks.filter(function(block) {
// Viewport: [--]
// Block: [--]
if (block.startPosition.isAfter(endPosition))
return false;
// Viewport: [--]
// Block: [--]
if (block.endPosition.isBefore(startPosition))
return false;
return true;
});
for (var block of basicBlocks) {
var key = block.startOffset + ":" + block.endOffset;
var hasKey = this._basicBlockMarkers.has(key);
var hasExecuted = block.hasExecuted;
if (hasKey && hasExecuted)
this._clearRangeForBasicBlockMarker(key);
else if (!hasKey && !hasExecuted) {
var marker = this._highlightTextForBasicBlock(block);
this._basicBlockMarkers.set(key, marker);
}
}
var totalTime = Date.now() - startTime;
var timeoutTime = Number.constrain(30 * totalTime, 500, 5000);
this._timeoutIdentifier = setTimeout(this.insertAnnotations.bind(this), timeoutTime);
}.bind(this));
}
_highlightTextForBasicBlock(basicBlock)
{
console.assert(basicBlock.startOffset <= basicBlock.endOffset && basicBlock.startOffset >= 0 && basicBlock.endOffset >= 0, "" + basicBlock.startOffset + ":" + basicBlock.endOffset);
console.assert(!basicBlock.hasExecuted);
let startPosition = this.sourceCodeTextEditor.originalPositionToCurrentPosition(basicBlock.startPosition);
let endPosition = this.sourceCodeTextEditor.originalPositionToCurrentPosition(basicBlock.endPosition);
if (this._isTextRangeOnlyClosingBrace(startPosition, endPosition))
return null;
return this.sourceCodeTextEditor.addStyleToTextRange(startPosition, endPosition, WI.BasicBlockAnnotator.HasNotExecutedClassName);
}
_isTextRangeOnlyClosingBrace(startPosition, endPosition)
{
var isOnlyClosingBrace = /^\s*\}$/;
return isOnlyClosingBrace.test(this.sourceCodeTextEditor.getTextInRange(startPosition, endPosition));
}
_clearRangeForBasicBlockMarker(key)
{
console.assert(this._basicBlockMarkers.has(key));
var marker = this._basicBlockMarkers.get(key);
if (marker)
marker.clear();
this._basicBlockMarkers.delete(key);
}
};
WI.BasicBlockAnnotator.HasNotExecutedClassName = "basic-block-has-not-executed";