169 lines
6.4 KiB
JavaScript
169 lines
6.4 KiB
JavaScript
/*
|
|
* 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";
|