Revision: 8149
Author: [email protected]
Date: Wed Jun 1 10:05:35 2011
Log: Fix Issue 1320: LiveEdit: text differencer fails with out of
memory on large files
Review URL: http://codereview.chromium.org/7080029
http://code.google.com/p/v8/source/detail?r=8149
Modified:
/branches/bleeding_edge/src/liveedit-debugger.js
/branches/bleeding_edge/src/liveedit.cc
/branches/bleeding_edge/src/liveedit.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/test/cctest/test-liveedit.cc
=======================================
--- /branches/bleeding_edge/src/liveedit-debugger.js Tue Jan 11 06:55:47
2011
+++ /branches/bleeding_edge/src/liveedit-debugger.js Wed Jun 1 10:05:35
2011
@@ -988,7 +988,11 @@
this.SetScriptSource = SetScriptSource;
function CompareStrings(s1, s2) {
- return %LiveEditCompareStrings(s1, s2);
+ try {
+ return %LiveEditCompareStrings(s1, s2);
+ } catch (e) {
+ throw new Failure("Failed to calculate text difference: " +
String(e));
+ }
}
// Applies the change to the script.
=======================================
--- /branches/bleeding_edge/src/liveedit.cc Tue May 31 13:58:21 2011
+++ /branches/bleeding_edge/src/liveedit.cc Wed Jun 1 10:05:35 2011
@@ -58,19 +58,65 @@
ASSERT(!no_failure.is_null());
USE(no_failure);
}
+
+// Creates string expression and throws it in V8 isolate.
+void ThrowStringException(const char* str, Isolate* isolate) {
+ Vector<const char> str_vector(str, strlen(str));
+ MaybeObject* maybe_exception =
+ isolate->heap()->AllocateStringFromAscii(str_vector);
+ Object* exception;
+ if (maybe_exception->ToObject(&exception)) {
+ isolate->Throw(exception, NULL);
+ }
+}
// A simple implementation of dynamic programming algorithm. It solves
-// the problem of finding the difference of 2 arrays. It uses a table of
results
-// of subproblems. Each cell contains a number together with 2-bit flag
+// the problem of finding the difference of 2 arrays. It uses a table of
+// results of subproblems. Each cell contains a number together with 2-bit
flag
// that helps building the chunk list.
class Differencer {
public:
- explicit Differencer(Comparator::Input* input)
- : input_(input), len1_(input->GetLength1()),
len2_(input->GetLength2()) {
- buffer_ = NewArray<int>(len1_ * len2_);
- }
+ explicit Differencer(Comparator::Input* input, Isolate* isolate,
+ bool* has_exception)
+ : input_(input), buffer_(NULL), len1_(input->GetLength1()),
+ len2_(input->GetLength2()) {
+ // Put multiply result into a bigger integer.
+ int64_t multiply =
+ static_cast<int64_t>(len1_) * static_cast<int64_t>(len2_);
+
+ // Maximum table size is max_int. Within this limit it's easy to check
+ // for multiply overflow. Should we support bigger numbers?
+ if (multiply > kMaxInt) {
+ ThrowStringException("Too many lines: differencer table size is too
big",
+ isolate);
+ *has_exception = true;
+ return;
+ }
+ multiply *= sizeof(int); // NOLINT
+ size_t size = multiply;
+ if (size != multiply) {
+ // Shouldn't be reachable.
+ ThrowStringException(
+ "Too many lines: "
+ "differencer table buffer size doesn't fit into size_t",
isolate);
+ *has_exception = true;
+ return;
+ }
+ void* p = malloc(size);
+ if (p == NULL) {
+ ThrowStringException(
+ "Too many lines: not enough memory for differencer table buffer",
+ isolate);
+ *has_exception = true;
+ return;
+ }
+ buffer_ = reinterpret_cast<int*>(p);
+ }
+
~Differencer() {
- DeleteArray(buffer_);
+ if (buffer_ != NULL) {
+ free(buffer_);
+ }
}
void Initialize() {
@@ -259,12 +305,18 @@
};
-void Comparator::CalculateDifference(Comparator::Input* input,
- Comparator::Output* result_writer) {
- Differencer differencer(input);
+MUST_USE_RESULT MaybeObject/*<void>*/* Comparator::CalculateDifference(
+ Comparator::Input* input, Comparator::Output* result_writer,
+ Isolate* isolate) {
+ bool has_exception = false;
+ Differencer differencer(input, isolate, &has_exception);
+ if (has_exception) {
+ return Failure::Exception();
+ }
differencer.Initialize();
differencer.FillTable();
differencer.SaveResult(result_writer);
+ return Smi::FromInt(0); // Unused value.
}
@@ -524,9 +576,10 @@
public:
TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
LineEndsWrapper line_ends2,
- Handle<String> s1, Handle<String> s2)
+ Handle<String> s1, Handle<String> s2,
+ Isolate* isolate)
: line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
- subrange_offset1_(0), subrange_offset2_(0) {
+ subrange_offset1_(0), subrange_offset2_(0), isolate_(isolate) {
}
void AddChunk(int line_pos1, int line_pos2, int line_len1, int
line_len2) {
@@ -547,7 +600,12 @@
TokensCompareOutput tokens_output(&array_writer_, char_pos1,
char_pos2);
- Comparator::CalculateDifference(&tokens_input, &tokens_output);
+ MaybeObject* res = Comparator::CalculateDifference(&tokens_input,
+ &tokens_output, isolate_);
+ if (res->IsFailure()) {
+ // We asked for a small amount of memory, this should not happen.
+ UNREACHABLE();
+ }
} else {
array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
}
@@ -573,11 +631,14 @@
Handle<String> s2_;
int subrange_offset1_;
int subrange_offset2_;
+ Isolate* isolate_;
};
-Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
- Handle<String> s2) {
+MUST_USE_RESULT MaybeObject/*<JSArray>*/* LiveEdit::CompareStrings(
+ Handle<String> s1, Handle<String> s2) {
+
+ Isolate* isolate = Isolate::Current();
s1 = FlattenGetString(s1);
s2 = FlattenGetString(s2);
@@ -585,13 +646,18 @@
LineEndsWrapper line_ends2(s2);
LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
- TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
+ TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2,
+ isolate);
NarrowDownInput(&input, &output);
- Comparator::CalculateDifference(&input, &output);
-
- return output.GetResult();
+ MaybeObject* result = Comparator::CalculateDifference(&input, &output,
+ isolate);
+ if (result->IsFailure()) {
+ return result;
+ }
+
+ return *output.GetResult();
}
=======================================
--- /branches/bleeding_edge/src/liveedit.h Tue May 31 13:58:21 2011
+++ /branches/bleeding_edge/src/liveedit.h Wed Jun 1 10:05:35 2011
@@ -135,8 +135,8 @@
// Compares 2 strings line-by-line, then token-wise and returns diff in
form
// of array of triplets (pos1, pos1_end, pos2_end) describing list
// of diff chunks.
- static Handle<JSArray> CompareStrings(Handle<String> s1,
- Handle<String> s2);
+ static MUST_USE_RESULT MaybeObject/*<JSArray>*/* CompareStrings(
+ Handle<String> s1, Handle<String> s2);
};
@@ -168,8 +168,8 @@
};
// Finds the difference between 2 arrays of elements.
- static void CalculateDifference(Input* input,
- Output* result_writer);
+ static MUST_USE_RESULT MaybeObject/*<void>*/* CalculateDifference(
+ Input* input, Output* result_writer, Isolate* isolate);
};
#endif // ENABLE_DEBUGGER_SUPPORT
=======================================
--- /branches/bleeding_edge/src/runtime.cc Tue May 31 09:38:40 2011
+++ /branches/bleeding_edge/src/runtime.cc Wed Jun 1 10:05:35 2011
@@ -11571,7 +11571,7 @@
CONVERT_ARG_CHECKED(String, s1, 0);
CONVERT_ARG_CHECKED(String, s2, 1);
- return *LiveEdit::CompareStrings(s1, s2);
+ return LiveEdit::CompareStrings(s1, s2);
}
=======================================
--- /branches/bleeding_edge/test/cctest/test-liveedit.cc Tue May 31
13:58:21 2011
+++ /branches/bleeding_edge/test/cctest/test-liveedit.cc Wed Jun 1
10:05:35 2011
@@ -95,12 +95,18 @@
int expected_diff_parameter = -1) {
StringCompareInput input(s1, s2);
- ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT);
+ Isolate* isolate = Isolate::Current();
+ ZoneScope zone_scope(isolate, DELETE_ON_EXIT);
DiffChunkStruct* first_chunk;
ListDiffOutputWriter writer(&first_chunk);
- Comparator::CalculateDifference(&input, &writer);
+ {
+ MaybeObject* maybe_result =
+ Comparator::CalculateDifference(&input, &writer, isolate);
+ ASSERT_EQ(maybe_result->IsFailure(), false);
+ USE(maybe_result);
+ }
int len1 = StrLength(s1);
int len2 = StrLength(s2);
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev