Only record one in n line endings to save space.
R=yangguo@chromium.org BUG= Review URL: https://codereview.chromium.org/1137683003 Cr-Commit-Position: refs/heads/master@{#28837}
This commit is contained in:
parent
5eafd7a3d9
commit
b3d4bce593
@ -2552,8 +2552,7 @@ void Debug::OnCompileError(Handle<Script> script) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Debug::OnDebugBreak(Handle<Object> break_points_hit,
|
void Debug::OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue) {
|
||||||
bool auto_continue) {
|
|
||||||
// The caller provided for DebugScope.
|
// The caller provided for DebugScope.
|
||||||
AssertDebugContext();
|
AssertDebugContext();
|
||||||
// Bail out if there is no listener for this event
|
// Bail out if there is no listener for this event
|
||||||
|
@ -511,18 +511,9 @@ class CaptureStackTraceHelper {
|
|||||||
// line_number is already shifted by the script_line_offset.
|
// line_number is already shifted by the script_line_offset.
|
||||||
int relative_line_number = line_number - script_line_offset;
|
int relative_line_number = line_number - script_line_offset;
|
||||||
if (!column_key_.is_null() && relative_line_number >= 0) {
|
if (!column_key_.is_null() && relative_line_number >= 0) {
|
||||||
Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
|
int column = Script::GetColumnNumber(script, position);
|
||||||
int start = (relative_line_number == 0) ? 0 :
|
|
||||||
Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
|
|
||||||
int column_offset = position - start;
|
|
||||||
if (relative_line_number == 0) {
|
|
||||||
// For the case where the code is on the same line as the script
|
|
||||||
// tag.
|
|
||||||
column_offset += script->column_offset()->value();
|
|
||||||
}
|
|
||||||
JSObject::AddProperty(stack_frame, column_key_,
|
JSObject::AddProperty(stack_frame, column_key_,
|
||||||
handle(Smi::FromInt(column_offset + 1), isolate_),
|
handle(Smi::FromInt(column + 1), isolate_), NONE);
|
||||||
NONE);
|
|
||||||
}
|
}
|
||||||
JSObject::AddProperty(stack_frame, line_key_,
|
JSObject::AddProperty(stack_frame, line_key_,
|
||||||
handle(Smi::FromInt(line_number + 1), isolate_),
|
handle(Smi::FromInt(line_number + 1), isolate_),
|
||||||
|
@ -305,6 +305,13 @@ macro ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(tab
|
|||||||
# Must match OrderedHashTable::kNotFound.
|
# Must match OrderedHashTable::kNotFound.
|
||||||
define NOT_FOUND = -1;
|
define NOT_FOUND = -1;
|
||||||
|
|
||||||
|
# Line ends array constants - see v8::internal::Script
|
||||||
|
define REDUCTION_INDEX = 0;
|
||||||
|
define NUMBER_OF_LINES_INDEX = 1;
|
||||||
|
define FIRST_LINE_END_INDEX = 2;
|
||||||
|
define ASCII_NL = 10;
|
||||||
|
define ASCII_CR = 13;
|
||||||
|
|
||||||
# Check whether debug is active.
|
# Check whether debug is active.
|
||||||
define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
|
define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
|
||||||
macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function));
|
macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function));
|
||||||
|
131
src/messages.js
131
src/messages.js
@ -40,6 +40,7 @@ var InternalArray = utils.InternalArray;
|
|||||||
var ObjectDefineProperty = utils.ObjectDefineProperty;
|
var ObjectDefineProperty = utils.ObjectDefineProperty;
|
||||||
|
|
||||||
var ArrayJoin;
|
var ArrayJoin;
|
||||||
|
var MathFloor;
|
||||||
var ObjectToString;
|
var ObjectToString;
|
||||||
var StringCharAt;
|
var StringCharAt;
|
||||||
var StringIndexOf;
|
var StringIndexOf;
|
||||||
@ -47,6 +48,7 @@ var StringSubstring;
|
|||||||
|
|
||||||
utils.Import(function(from) {
|
utils.Import(function(from) {
|
||||||
ArrayJoin = from.ArrayJoin;
|
ArrayJoin = from.ArrayJoin;
|
||||||
|
MathFloor = from.MathFloor;
|
||||||
ObjectToString = from.ObjectToString;
|
ObjectToString = from.ObjectToString;
|
||||||
StringCharAt = from.StringCharAt;
|
StringCharAt = from.StringCharAt;
|
||||||
StringIndexOf = from.StringIndexOf;
|
StringIndexOf = from.StringIndexOf;
|
||||||
@ -203,41 +205,94 @@ function GetSourceLine(message) {
|
|||||||
return location.sourceText();
|
return location.sourceText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Newlines(source, from, to, reduction) {
|
||||||
|
var newLines = new InternalArray();
|
||||||
|
if (!IS_STRING(source)) return newLines;
|
||||||
|
|
||||||
|
var length = source.length;
|
||||||
|
for (; from < to && from < length && newLines.length < reduction - 1
|
||||||
|
; ++from) {
|
||||||
|
var c = %_StringCharCodeAt(source, from);
|
||||||
|
if (c == ASCII_CR) {
|
||||||
|
if (from < length - 1) {
|
||||||
|
var c2 = %_StringCharCodeAt(source, from + 1);
|
||||||
|
if (c2 == ASCII_NL) {
|
||||||
|
from++; // CR-LF counts as one newline.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newLines.push(from);
|
||||||
|
} else if (c == ASCII_NL) {
|
||||||
|
newLines.push(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End-of-file virtual end-of-line.
|
||||||
|
if (to >= length) {
|
||||||
|
var last = length != 0 ? %_StringCharCodeAt(source, length - 1) : 0;
|
||||||
|
if (last != ASCII_NL && last != ASCII_CR) newLines.push(source.length - 1);
|
||||||
|
}
|
||||||
|
return newLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ScriptLineEnd(line) {
|
||||||
|
if (line < 0) return -1;
|
||||||
|
var source = this.source;
|
||||||
|
if (!IS_STRING(source)) return -1;
|
||||||
|
var line_ends = this.line_ends;
|
||||||
|
var reduction = line_ends[REDUCTION_INDEX];
|
||||||
|
var index = MathFloor(line / reduction) + FIRST_LINE_END_INDEX;
|
||||||
|
if (index >= line_ends.length) return -1;
|
||||||
|
var position = line_ends[index];
|
||||||
|
if (line % reduction == 0) return position;
|
||||||
|
var lines = Newlines(source, position + 1, source.length, reduction);
|
||||||
|
return lines[line % reduction - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a line number given a specific source position.
|
* Find a line number given a specific source position.
|
||||||
* @param {number} position The source position.
|
* @param {number} position The source position.
|
||||||
* @return {number} 0 if input too small, -1 if input too large,
|
* @return {number} -1 if position too large, else the 0-based line number.
|
||||||
else the line number.
|
|
||||||
*/
|
*/
|
||||||
function ScriptLineFromPosition(position) {
|
function ScriptLineFromPosition(position) {
|
||||||
var lower = 0;
|
var source = this.source;
|
||||||
var upper = this.lineCount() - 1;
|
if (!IS_STRING(source)) return -1;
|
||||||
|
|
||||||
var line_ends = this.line_ends;
|
var line_ends = this.line_ends;
|
||||||
|
var lower = FIRST_LINE_END_INDEX;
|
||||||
|
var upper = line_ends.length - 1;
|
||||||
|
|
||||||
// We'll never find invalid positions so bail right away.
|
var reduction = line_ends[REDUCTION_INDEX];
|
||||||
if (position > line_ends[upper]) {
|
// This '>' would normally be a '>=', but due to {}-less 'with' statements in
|
||||||
return -1;
|
// top-level code we sometimes encounter code positions that are one character
|
||||||
}
|
// after the end of the source. See comment in Rewriter::Rewrite.
|
||||||
|
if (position > source.length) return -1;
|
||||||
|
|
||||||
// This means we don't have to safe-guard indexing line_ends[i - 1].
|
var index = 0;
|
||||||
if (position <= line_ends[0]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binary search to find line # from position range.
|
// Binary search to find line # from position range.
|
||||||
while (upper >= 1) {
|
if (position > line_ends[upper]) {
|
||||||
var i = (lower + upper) >> 1;
|
index = upper;
|
||||||
|
} else {
|
||||||
if (position > line_ends[i]) {
|
// Invariant: position > line_ends[lower]
|
||||||
lower = i + 1;
|
// Invariant: position <= line_ends[upper]
|
||||||
} else if (position <= line_ends[i - 1]) {
|
while (lower + 1 < upper) {
|
||||||
upper = i - 1;
|
// Since they differ by at least 2, i must be different from both
|
||||||
} else {
|
// upper or lower.
|
||||||
return i;
|
var i = (lower + upper) >> 1;
|
||||||
|
if (position > line_ends[i]) {
|
||||||
|
lower = i;
|
||||||
|
} else {
|
||||||
|
upper = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
index = lower;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
var line = (index - FIRST_LINE_END_INDEX) * reduction;
|
||||||
|
return line +
|
||||||
|
Newlines(source, line_ends[index] + 1, position, reduction).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,14 +305,19 @@ function ScriptLineFromPosition(position) {
|
|||||||
*/
|
*/
|
||||||
function ScriptLocationFromPosition(position,
|
function ScriptLocationFromPosition(position,
|
||||||
include_resource_offset) {
|
include_resource_offset) {
|
||||||
|
// Get zero-based line number.
|
||||||
var line = this.lineFromPosition(position);
|
var line = this.lineFromPosition(position);
|
||||||
if (line == -1) return null;
|
if (line == -1) return null;
|
||||||
|
|
||||||
// Determine start, end and column.
|
// Determine start, end and column.
|
||||||
var line_ends = this.line_ends;
|
var start = this.lineEnd(line) + 1;
|
||||||
var start = line == 0 ? 0 : line_ends[line - 1] + 1;
|
// End will be used for substr, so make it non-inclusive.
|
||||||
var end = line_ends[line];
|
var end = this.lineEnd(line + 1) + 1;
|
||||||
if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') {
|
if (end > this.source.length) end = this.source.length;
|
||||||
|
// But trim the newline if there is one (there might not be at EOF).
|
||||||
|
while (end > start) {
|
||||||
|
var trim_char = %_CallFunction(this.source, end - 1, StringCharAt);
|
||||||
|
if (trim_char != '\n' && trim_char != '\r') break;
|
||||||
end--;
|
end--;
|
||||||
}
|
}
|
||||||
var column = position - start;
|
var column = position - start;
|
||||||
@ -317,7 +377,7 @@ function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.locationFromPosition(
|
return this.locationFromPosition(
|
||||||
this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
|
this.lineEnd(offset_line + line) + 1 + column); // line > 0 here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,15 +411,14 @@ function ScriptSourceSlice(opt_from_line, opt_to_line) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var line_ends = this.line_ends;
|
var from_position = this.lineEnd(from_line) + 1;
|
||||||
var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
|
var to_position = this.lineEnd(to_line) + 1;
|
||||||
var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
|
|
||||||
|
|
||||||
// Return a source slice with line numbers re-adjusted to the resource.
|
// Return a source slice with line numbers re-adjusted to the resource.
|
||||||
return new SourceSlice(this,
|
return new SourceSlice(this,
|
||||||
from_line + this.line_offset,
|
from_line + this.line_offset,
|
||||||
to_line + this.line_offset,
|
to_line + this.line_offset,
|
||||||
from_position, to_position);
|
from_position, to_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -377,9 +436,8 @@ function ScriptSourceLine(opt_line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the source line.
|
// Return the source line.
|
||||||
var line_ends = this.line_ends;
|
var start = this.lineEnd(line) + 1;
|
||||||
var start = line == 0 ? 0 : line_ends[line - 1] + 1;
|
var end = this.lineEnd(line + 1);
|
||||||
var end = line_ends[line];
|
|
||||||
return %_CallFunction(this.source, start, end, StringSubstring);
|
return %_CallFunction(this.source, start, end, StringSubstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +449,7 @@ function ScriptSourceLine(opt_line) {
|
|||||||
*/
|
*/
|
||||||
function ScriptLineCount() {
|
function ScriptLineCount() {
|
||||||
// Return number of source lines.
|
// Return number of source lines.
|
||||||
return this.line_ends.length;
|
return this.line_ends[NUMBER_OF_LINES_INDEX];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -426,7 +484,8 @@ utils.SetUpLockedPrototype(Script, [
|
|||||||
"sourceSlice", ScriptSourceSlice,
|
"sourceSlice", ScriptSourceSlice,
|
||||||
"sourceLine", ScriptSourceLine,
|
"sourceLine", ScriptSourceLine,
|
||||||
"lineCount", ScriptLineCount,
|
"lineCount", ScriptLineCount,
|
||||||
"nameOrSourceURL", ScriptNameOrSourceURL
|
"nameOrSourceURL", ScriptNameOrSourceURL,
|
||||||
|
"lineEnd", ScriptLineEnd
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
184
src/objects.cc
184
src/objects.cc
@ -8901,18 +8901,62 @@ static void CalculateLineEndsImpl(Isolate* isolate,
|
|||||||
Vector<const SourceChar> src,
|
Vector<const SourceChar> src,
|
||||||
bool include_ending_line) {
|
bool include_ending_line) {
|
||||||
const int src_len = src.length();
|
const int src_len = src.length();
|
||||||
|
bool exotic_newlines = false;
|
||||||
|
if (include_ending_line) {
|
||||||
|
// Initally assume reduction is 1, ie all line endings are in the array.
|
||||||
|
DCHECK_EQ(line_ends->length(), Script::kReductionIndex);
|
||||||
|
line_ends->Add(1);
|
||||||
|
// Write a placeholder for the number-of-lines indicator.
|
||||||
|
DCHECK_EQ(line_ends->length(), Script::kNumberOfLinesIndex);
|
||||||
|
line_ends->Add(0);
|
||||||
|
DCHECK_EQ(line_ends->length(), Script::kFirstLineEndIndex);
|
||||||
|
// There's a fictional newline just before the first character. This
|
||||||
|
// simplifies a lot of things.
|
||||||
|
line_ends->Add(-1);
|
||||||
|
}
|
||||||
UnicodeCache* cache = isolate->unicode_cache();
|
UnicodeCache* cache = isolate->unicode_cache();
|
||||||
for (int i = 0; i < src_len - 1; i++) {
|
for (int i = 0; i < src_len - 1; i++) {
|
||||||
SourceChar current = src[i];
|
SourceChar current = src[i];
|
||||||
SourceChar next = src[i + 1];
|
SourceChar next = src[i + 1];
|
||||||
if (cache->IsLineTerminatorSequence(current, next)) line_ends->Add(i);
|
if (cache->IsLineTerminatorSequence(current, next)) {
|
||||||
|
if (current != '\n' && current != '\r') exotic_newlines = true;
|
||||||
|
line_ends->Add(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_len > 0 && cache->IsLineTerminatorSequence(src[src_len - 1], 0)) {
|
int last_posn = src_len - 1;
|
||||||
line_ends->Add(src_len - 1);
|
if (last_posn >= 0 && cache->IsLineTerminatorSequence(src[last_posn], 0)) {
|
||||||
|
if (src[last_posn] != '\n' && src[last_posn] != '\r')
|
||||||
|
exotic_newlines = true;
|
||||||
|
line_ends->Add(last_posn);
|
||||||
} else if (include_ending_line) {
|
} else if (include_ending_line) {
|
||||||
// Even if the last line misses a line end, it is counted.
|
// Even if the last line misses a line end, it is counted. Because we
|
||||||
line_ends->Add(src_len);
|
// sometimes use character positions that are one beyond the end of the
|
||||||
|
// source (see Rewriter::Rewrite) we set the newline one beyond that.
|
||||||
|
// This is used for substr calculations, which trims to string length,
|
||||||
|
// so it's harmless.
|
||||||
|
line_ends->Add(last_posn + 1);
|
||||||
|
}
|
||||||
|
if (include_ending_line) {
|
||||||
|
// Update number of lines in script.
|
||||||
|
int lines = line_ends->length() - (Script::kFirstLineEndIndex + 1);
|
||||||
|
line_ends->Set(Script::kNumberOfLinesIndex, lines);
|
||||||
|
// Abuse some flags. The bots will run with a good variety of these flags,
|
||||||
|
// giving better coverage for the reduction code.
|
||||||
|
bool always_reduce = FLAG_always_opt;
|
||||||
|
bool never_reduce = !FLAG_crankshaft;
|
||||||
|
if (!never_reduce && !exotic_newlines &&
|
||||||
|
(always_reduce ||
|
||||||
|
(line_ends->length() > 5 && line_ends->length() * 8 > src_len / 12))) {
|
||||||
|
// If the line-ends array (8 bytes per entry) is larger than about 8%
|
||||||
|
// of the source length, then we reduce it to save memory. This won't
|
||||||
|
// trigger if lines are > 100 characters on average. If it triggers, then
|
||||||
|
// the goal is for it to take only 3% of the source size.
|
||||||
|
int reduction =
|
||||||
|
always_reduce ? 2 : (line_ends->length() * 8 * 33 / src_len);
|
||||||
|
DCHECK(reduction > 1);
|
||||||
|
line_ends->Set(Script::kReductionIndex, reduction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8941,12 +8985,30 @@ Handle<FixedArray> String::CalculateLineEnds(Handle<String> src,
|
|||||||
include_ending_line);
|
include_ending_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int line_count = line_ends.length();
|
if (include_ending_line) {
|
||||||
Handle<FixedArray> array = isolate->factory()->NewFixedArray(line_count);
|
const int kReductionIndex = Script::kReductionIndex;
|
||||||
for (int i = 0; i < line_count; i++) {
|
const int kFirstLineEndIndex = Script::kFirstLineEndIndex;
|
||||||
array->set(i, Smi::FromInt(line_ends[i]));
|
int line_count = line_ends.length() - kFirstLineEndIndex;
|
||||||
|
int reduction = line_ends[kReductionIndex];
|
||||||
|
int reduced_lines = (line_count + reduction - 1) / reduction;
|
||||||
|
Handle<FixedArray> array =
|
||||||
|
isolate->factory()->NewFixedArray(kFirstLineEndIndex + reduced_lines);
|
||||||
|
for (int i = 0; i < kFirstLineEndIndex; i++) {
|
||||||
|
array->set(i, Smi::FromInt(line_ends[i]));
|
||||||
|
}
|
||||||
|
int j = kFirstLineEndIndex;
|
||||||
|
for (int i = 0; i < line_count; i += reduction, ++j) {
|
||||||
|
array->set(j, Smi::FromInt(line_ends[i + kFirstLineEndIndex]));
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
} else {
|
||||||
|
Handle<FixedArray> array =
|
||||||
|
isolate->factory()->NewFixedArray(line_ends.length());
|
||||||
|
for (int i = 0; i < line_ends.length(); i++) {
|
||||||
|
array->set(i, Smi::FromInt(line_ends[i]));
|
||||||
|
}
|
||||||
|
return array;
|
||||||
}
|
}
|
||||||
return array;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -10304,41 +10366,113 @@ void Script::InitLineEnds(Handle<Script> script) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int CountForwardNNewlines(Handle<Script> script, int block_position,
|
||||||
|
int n) {
|
||||||
|
int position = block_position;
|
||||||
|
Handle<Object> source_object(script->source(), script->GetIsolate());
|
||||||
|
if (!source_object->IsString() || n == 0) return position;
|
||||||
|
Handle<String> source(Handle<String>::cast(source_object));
|
||||||
|
int length = source->length();
|
||||||
|
for (int i = position; i < length; i++) {
|
||||||
|
uc16 current = source->Get(i);
|
||||||
|
if (current == '\r') {
|
||||||
|
n--;
|
||||||
|
if (i + 1 < length && source->Get(i + 1) == '\n') i++;
|
||||||
|
} else if (current == '\n') {
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
if (n == 0) return i + 1;
|
||||||
|
}
|
||||||
|
if (n == 1 && length > 0) {
|
||||||
|
uc16 last = source->Get(length - 1);
|
||||||
|
if (last != '\n' && last != '\r') return length;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int Script::GetColumnNumber(Handle<Script> script, int code_pos) {
|
int Script::GetColumnNumber(Handle<Script> script, int code_pos) {
|
||||||
|
// Get zero-based line number.
|
||||||
int line_number = GetLineNumber(script, code_pos);
|
int line_number = GetLineNumber(script, code_pos);
|
||||||
if (line_number == -1) return -1;
|
if (line_number == -1) return -1;
|
||||||
|
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
FixedArray* line_ends_array = FixedArray::cast(script->line_ends());
|
FixedArray* line_ends_array = FixedArray::cast(script->line_ends());
|
||||||
line_number = line_number - script->line_offset()->value();
|
line_number = line_number - script->line_offset()->value();
|
||||||
if (line_number == 0) return code_pos + script->column_offset()->value();
|
int reduction = Smi::cast(line_ends_array->get(kReductionIndex))->value();
|
||||||
int prev_line_end_pos =
|
|
||||||
Smi::cast(line_ends_array->get(line_number - 1))->value();
|
int line_block_position =
|
||||||
return code_pos - (prev_line_end_pos + 1);
|
Smi::cast(line_ends_array->get(line_number / reduction +
|
||||||
|
kFirstLineEndIndex))->value() +
|
||||||
|
1;
|
||||||
|
|
||||||
|
int line_position = CountForwardNNewlines(script, line_block_position,
|
||||||
|
line_number % reduction);
|
||||||
|
if (line_number == 0) line_position = -script->column_offset()->value();
|
||||||
|
return code_pos - line_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Zero-based line number, calculated from UTF16 character position.
|
||||||
int Script::GetLineNumberWithArray(int code_pos) {
|
int Script::GetLineNumberWithArray(int code_pos) {
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
DCHECK(line_ends()->IsFixedArray());
|
DCHECK(line_ends()->IsFixedArray());
|
||||||
FixedArray* line_ends_array = FixedArray::cast(line_ends());
|
FixedArray* line_ends_array = FixedArray::cast(line_ends());
|
||||||
int line_ends_len = line_ends_array->length();
|
int line_ends_len = line_ends_array->length();
|
||||||
if (line_ends_len == 0) return -1;
|
if (line_ends_len == 0) return -1; // This happens if there is no source.
|
||||||
|
// There's always at least one line ending: A fictional newline just before
|
||||||
|
// the start.
|
||||||
|
DCHECK_GE(line_ends_len, kFirstLineEndIndex + 1);
|
||||||
|
int lower = kFirstLineEndIndex;
|
||||||
|
int upper = line_ends_len - 1;
|
||||||
|
|
||||||
if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) {
|
if (code_pos < 0) return -1;
|
||||||
return line_offset()->value();
|
int index = 0;
|
||||||
|
|
||||||
|
if (code_pos > Smi::cast(line_ends_array->get(upper))->value()) {
|
||||||
|
index = upper;
|
||||||
|
} else {
|
||||||
|
while (lower + 1 < upper) {
|
||||||
|
DCHECK_LE(Smi::cast(line_ends_array->get(lower))->value(), code_pos);
|
||||||
|
DCHECK_LE(code_pos, Smi::cast(line_ends_array->get(upper))->value());
|
||||||
|
int i = (lower + upper) >> 1;
|
||||||
|
DCHECK(lower != i && upper != i);
|
||||||
|
if ((Smi::cast(line_ends_array->get(i)))->value() >= code_pos) {
|
||||||
|
upper = i;
|
||||||
|
} else {
|
||||||
|
lower = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = lower;
|
||||||
}
|
}
|
||||||
|
|
||||||
int left = 0;
|
int reduction = Smi::cast(line_ends_array->get(kReductionIndex))->value();
|
||||||
int right = line_ends_len;
|
int line_number = (index - kFirstLineEndIndex) * reduction;
|
||||||
while (int half = (right - left) / 2) {
|
|
||||||
if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) {
|
// We only saved an nth of the line ends in the array, because there were so
|
||||||
right -= half;
|
// many.
|
||||||
} else {
|
int start_of_earlier_line =
|
||||||
left += half;
|
Smi::cast(line_ends_array->get(index))->value() + 1;
|
||||||
|
|
||||||
|
if (reduction == 1 || !source()->IsString()) {
|
||||||
|
return line_number + line_offset()->value();
|
||||||
|
}
|
||||||
|
String* src = String::cast(source());
|
||||||
|
// This '>' would normally be a '>=', but due to {}-less 'with' statements in
|
||||||
|
// top-level code we sometimes encounter code positions that are one character
|
||||||
|
// after the end of the source. See comment in Rewriter::Rewrite.
|
||||||
|
if (code_pos > src->length()) return -1;
|
||||||
|
for (int i = start_of_earlier_line; i < src->length() && i < code_pos; i++) {
|
||||||
|
uc16 current = src->Get(i);
|
||||||
|
if (current == '\r') {
|
||||||
|
if (i < code_pos - 1 && i < src->length() - 1 && src->Get(i + 1) == '\n')
|
||||||
|
i++;
|
||||||
|
line_number++;
|
||||||
|
} else if (current == '\n') {
|
||||||
|
line_number++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return right + line_offset()->value();
|
return line_number + line_offset()->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6540,6 +6540,11 @@ class Script: public Struct {
|
|||||||
static const int kSourceMappingUrlOffset = kSourceUrlOffset + kPointerSize;
|
static const int kSourceMappingUrlOffset = kSourceUrlOffset + kPointerSize;
|
||||||
static const int kSize = kSourceMappingUrlOffset + kPointerSize;
|
static const int kSize = kSourceMappingUrlOffset + kPointerSize;
|
||||||
|
|
||||||
|
// Sync with constants in macros.py.
|
||||||
|
static const int kReductionIndex = 0;
|
||||||
|
static const int kNumberOfLinesIndex = 1;
|
||||||
|
static const int kFirstLineEndIndex = 2;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int GetLineNumberWithArray(int code_pos);
|
int GetLineNumberWithArray(int code_pos);
|
||||||
|
|
||||||
|
@ -235,6 +235,9 @@ bool Rewriter::Rewrite(ParseInfo* info) {
|
|||||||
// eval('with ({x:1}) x = 1');
|
// eval('with ({x:1}) x = 1');
|
||||||
// the end position of the function generated for executing the eval code
|
// the end position of the function generated for executing the eval code
|
||||||
// coincides with the end of the with scope which is the position of '1'.
|
// coincides with the end of the with scope which is the position of '1'.
|
||||||
|
// Note that this may mean the position is outside the source code
|
||||||
|
// completely if there is no terminal newline, curly brace, or semicolon,
|
||||||
|
// often the case for 'eval'.
|
||||||
int pos = function->end_position();
|
int pos = function->end_position();
|
||||||
VariableProxy* result_proxy =
|
VariableProxy* result_proxy =
|
||||||
processor.factory()->NewVariableProxy(result, pos);
|
processor.factory()->NewVariableProxy(result, pos);
|
||||||
|
@ -14301,6 +14301,32 @@ void AnalyzeStackInNativeCode(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ChangeNewlines(int kind, char* dest, size_t dest_len, const char* source) {
|
||||||
|
if (kind == 0) {
|
||||||
|
for (size_t i = 0; i <= strlen(source); i++) {
|
||||||
|
dest[i] = source[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i <= strlen(source); i++) {
|
||||||
|
char c = source[i];
|
||||||
|
if (c == '\n') {
|
||||||
|
if (kind == 1) {
|
||||||
|
*dest++ = '\r';
|
||||||
|
*dest++ = '\n';
|
||||||
|
} else {
|
||||||
|
// UTF-8 version of 0x2028 newline.
|
||||||
|
*dest++ = '\xe2';
|
||||||
|
*dest++ = '\x80';
|
||||||
|
*dest++ = '\xa8';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*dest++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tests the C++ StackTrace API.
|
// Tests the C++ StackTrace API.
|
||||||
// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
|
// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
|
||||||
// THREADED_TEST(CaptureStackTrace) {
|
// THREADED_TEST(CaptureStackTrace) {
|
||||||
@ -14314,50 +14340,61 @@ TEST(CaptureStackTrace) {
|
|||||||
v8::FunctionTemplate::New(isolate, AnalyzeStackInNativeCode));
|
v8::FunctionTemplate::New(isolate, AnalyzeStackInNativeCode));
|
||||||
LocalContext context(0, templ);
|
LocalContext context(0, templ);
|
||||||
|
|
||||||
// Test getting OVERVIEW information. Should ignore information that is not
|
for (int i = 0; i < 3; i++) {
|
||||||
// script name, function name, line number, and column offset.
|
// Test getting OVERVIEW information. Should ignore information that is not
|
||||||
const char *overview_source =
|
// script name, function name, line number, and column offset.
|
||||||
"function bar() {\n"
|
const char* overview_source =
|
||||||
" var y; AnalyzeStackInNativeCode(1);\n"
|
"function bar() {\n"
|
||||||
"}\n"
|
" var y; AnalyzeStackInNativeCode(1);\n"
|
||||||
"function foo() {\n"
|
"}\n"
|
||||||
"\n"
|
"function foo() {\n"
|
||||||
" bar();\n"
|
"\n"
|
||||||
"}\n"
|
" bar();\n"
|
||||||
"var x;eval('new foo();');";
|
"}\n"
|
||||||
v8::Handle<v8::String> overview_src =
|
"var x;eval('new foo();');";
|
||||||
v8::String::NewFromUtf8(isolate, overview_source);
|
size_t munged_length = strlen(overview_source) * 3 + 1;
|
||||||
v8::ScriptCompiler::Source script_source(overview_src,
|
char* overview_munged_source = new char[munged_length];
|
||||||
v8::ScriptOrigin(origin));
|
ChangeNewlines(i, overview_munged_source, munged_length, overview_source);
|
||||||
v8::Handle<Value> overview_result(
|
|
||||||
v8::ScriptCompiler::CompileUnbound(isolate, &script_source)
|
|
||||||
->BindToCurrentContext()
|
|
||||||
->Run());
|
|
||||||
CHECK(!overview_result.IsEmpty());
|
|
||||||
CHECK(overview_result->IsObject());
|
|
||||||
|
|
||||||
// Test getting DETAILED information.
|
v8::Handle<v8::String> overview_src =
|
||||||
const char *detailed_source =
|
v8::String::NewFromUtf8(isolate, overview_munged_source);
|
||||||
"function bat() {AnalyzeStackInNativeCode(2);\n"
|
delete[] overview_munged_source;
|
||||||
"}\n"
|
v8::ScriptCompiler::Source script_source(overview_src,
|
||||||
"\n"
|
v8::ScriptOrigin(origin));
|
||||||
"function baz() {\n"
|
v8::Handle<Value> overview_result(
|
||||||
" bat();\n"
|
v8::ScriptCompiler::CompileUnbound(isolate, &script_source)
|
||||||
"}\n"
|
->BindToCurrentContext()
|
||||||
"eval('new baz();');";
|
->Run());
|
||||||
v8::Handle<v8::String> detailed_src =
|
CHECK(!overview_result.IsEmpty());
|
||||||
v8::String::NewFromUtf8(isolate, detailed_source);
|
CHECK(overview_result->IsObject());
|
||||||
// Make the script using a non-zero line and column offset.
|
|
||||||
v8::Handle<v8::Integer> line_offset = v8::Integer::New(isolate, 3);
|
// Test getting DETAILED information.
|
||||||
v8::Handle<v8::Integer> column_offset = v8::Integer::New(isolate, 5);
|
const char* detailed_source =
|
||||||
v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
|
"function bat() {AnalyzeStackInNativeCode(2);\n"
|
||||||
v8::ScriptCompiler::Source script_source2(detailed_src, detailed_origin);
|
"}\n"
|
||||||
v8::Handle<v8::UnboundScript> detailed_script(
|
"\n"
|
||||||
v8::ScriptCompiler::CompileUnbound(isolate, &script_source2));
|
"function baz() {\n"
|
||||||
v8::Handle<Value> detailed_result(
|
" bat();\n"
|
||||||
detailed_script->BindToCurrentContext()->Run());
|
"}\n"
|
||||||
CHECK(!detailed_result.IsEmpty());
|
"eval('new baz();');";
|
||||||
CHECK(detailed_result->IsObject());
|
munged_length = strlen(detailed_source) * 3 + 1;
|
||||||
|
char* detailed_munged_source = new char[munged_length];
|
||||||
|
ChangeNewlines(i, detailed_munged_source, munged_length, detailed_source);
|
||||||
|
v8::Handle<v8::String> detailed_src =
|
||||||
|
v8::String::NewFromUtf8(isolate, detailed_munged_source);
|
||||||
|
delete[] detailed_munged_source;
|
||||||
|
// Make the script using a non-zero line and column offset.
|
||||||
|
v8::Handle<v8::Integer> line_offset = v8::Integer::New(isolate, 3);
|
||||||
|
v8::Handle<v8::Integer> column_offset = v8::Integer::New(isolate, 5);
|
||||||
|
v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
|
||||||
|
v8::ScriptCompiler::Source script_source2(detailed_src, detailed_origin);
|
||||||
|
v8::Handle<v8::UnboundScript> detailed_script(
|
||||||
|
v8::ScriptCompiler::CompileUnbound(isolate, &script_source2));
|
||||||
|
v8::Handle<Value> detailed_result(
|
||||||
|
detailed_script->BindToCurrentContext()->Run());
|
||||||
|
CHECK(!detailed_result.IsEmpty());
|
||||||
|
CHECK(detailed_result->IsObject());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,11 +63,11 @@ var comment_lines = 28;
|
|||||||
|
|
||||||
// This is the last position in the entire file (note: this equals
|
// This is the last position in the entire file (note: this equals
|
||||||
// file size of <debug-sourceinfo.js> - 1, since starting at 0).
|
// file size of <debug-sourceinfo.js> - 1, since starting at 0).
|
||||||
var last_position = 11337;
|
var last_position = 11591;
|
||||||
// This is the last line of entire file (note: starting at 0).
|
// This is the last line of entire file (note: starting at 0).
|
||||||
var last_line = 265;
|
var last_line = 268;
|
||||||
// This is the last column of last line (note: starting at 0 and +1, due
|
// This is the column of the last character (note: starting at 0) due to
|
||||||
// to trailing <LF>).
|
// final line having a trailing newline that is conceptually part of that line.
|
||||||
var last_column = 1;
|
var last_column = 1;
|
||||||
|
|
||||||
// This magic number is the length or the first line comment (actually number
|
// This magic number is the length or the first line comment (actually number
|
||||||
@ -250,7 +250,10 @@ assertEquals(158 + start_d, Debug.findFunctionSourceLocation(d, 17, 0).position)
|
|||||||
|
|
||||||
// Make sure invalid inputs work properly.
|
// Make sure invalid inputs work properly.
|
||||||
assertEquals(0, script.locationFromPosition(-1).line);
|
assertEquals(0, script.locationFromPosition(-1).line);
|
||||||
assertEquals(null, script.locationFromPosition(last_position + 1));
|
// We might expect last_position + 1 to be the first illegal position, but we
|
||||||
|
// sometimes generate character positions that are one past the last character.
|
||||||
|
// See Rewriter::Rewrite for details.
|
||||||
|
assertEquals(null, script.locationFromPosition(last_position + 2));
|
||||||
|
|
||||||
// Test last position.
|
// Test last position.
|
||||||
assertEquals(last_position, script.locationFromPosition(last_position).position);
|
assertEquals(last_position, script.locationFromPosition(last_position).position);
|
||||||
|
Loading…
Reference in New Issue
Block a user