// Copyright 2010 the V8 project 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. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // 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 // OWNER 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. #include "v8.h" #include "liveedit.h" #include "compiler.h" #include "oprofile-agent.h" #include "scopes.h" #include "global-handles.h" #include "debug.h" #include "memory.h" namespace v8 { namespace internal { #ifdef ENABLE_DEBUGGER_SUPPORT // 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 // that helps building the chunk list. class Differencer { public: explicit Differencer(Comparator::Input* input) : input_(input), len1_(input->getLength1()), len2_(input->getLength2()) { buffer_ = NewArray(len1_ * len2_); } ~Differencer() { DeleteArray(buffer_); } void Initialize() { int array_size = len1_ * len2_; for (int i = 0; i < array_size; i++) { buffer_[i] = kEmptyCellValue; } } // Makes sure that result for the full problem is calculated and stored // in the table together with flags showing a path through subproblems. void FillTable() { CompareUpToTail(0, 0); } void SaveResult(Comparator::Output* chunk_writer) { ResultWriter writer(chunk_writer); int pos1 = 0; int pos2 = 0; while (true) { if (pos1 < len1_) { if (pos2 < len2_) { Direction dir = get_direction(pos1, pos2); switch (dir) { case EQ: writer.eq(); pos1++; pos2++; break; case SKIP1: writer.skip1(1); pos1++; break; case SKIP2: case SKIP_ANY: writer.skip2(1); pos2++; break; default: UNREACHABLE(); } } else { writer.skip1(len1_ - pos1); break; } } else { if (len2_ != pos2) { writer.skip2(len2_ - pos2); } break; } } writer.close(); } private: Comparator::Input* input_; int* buffer_; int len1_; int len2_; enum Direction { EQ = 0, SKIP1, SKIP2, SKIP_ANY, MAX_DIRECTION_FLAG_VALUE = SKIP_ANY }; // Computes result for a subtask and optionally caches it in the buffer table. // All results values are shifted to make space for flags in the lower bits. int CompareUpToTail(int pos1, int pos2) { if (pos1 < len1_) { if (pos2 < len2_) { int cached_res = get_value4(pos1, pos2); if (cached_res == kEmptyCellValue) { Direction dir; int res; if (input_->equals(pos1, pos2)) { res = CompareUpToTail(pos1 + 1, pos2 + 1); dir = EQ; } else { int res1 = CompareUpToTail(pos1 + 1, pos2) + (1 << kDirectionSizeBits); int res2 = CompareUpToTail(pos1, pos2 + 1) + (1 << kDirectionSizeBits); if (res1 == res2) { res = res1; dir = SKIP_ANY; } else if (res1 < res2) { res = res1; dir = SKIP1; } else { res = res2; dir = SKIP2; } } set_value4_and_dir(pos1, pos2, res, dir); cached_res = res; } return cached_res; } else { return (len1_ - pos1) << kDirectionSizeBits; } } else { return (len2_ - pos2) << kDirectionSizeBits; } } inline int& get_cell(int i1, int i2) { return buffer_[i1 + i2 * len1_]; } // Each cell keeps a value plus direction. Value is multiplied by 4. void set_value4_and_dir(int i1, int i2, int value4, Direction dir) { ASSERT((value4 & kDirectionMask) == 0); get_cell(i1, i2) = value4 | dir; } int get_value4(int i1, int i2) { return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask); } Direction get_direction(int i1, int i2) { return static_cast(get_cell(i1, i2) & kDirectionMask); } static const int kDirectionSizeBits = 2; static const int kDirectionMask = (1 << kDirectionSizeBits) - 1; static const int kEmptyCellValue = -1 << kDirectionSizeBits; // This method only holds static assert statement (unfortunately you cannot // place one in class scope). void StaticAssertHolder() { STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits)); } class ResultWriter { public: explicit ResultWriter(Comparator::Output* chunk_writer) : chunk_writer_(chunk_writer), pos1_(0), pos2_(0), pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) { } void eq() { FlushChunk(); pos1_++; pos2_++; } void skip1(int len1) { StartChunk(); pos1_ += len1; } void skip2(int len2) { StartChunk(); pos2_ += len2; } void close() { FlushChunk(); } private: Comparator::Output* chunk_writer_; int pos1_; int pos2_; int pos1_begin_; int pos2_begin_; bool has_open_chunk_; void StartChunk() { if (!has_open_chunk_) { pos1_begin_ = pos1_; pos2_begin_ = pos2_; has_open_chunk_ = true; } } void FlushChunk() { if (has_open_chunk_) { chunk_writer_->AddChunk(pos1_begin_, pos2_begin_, pos1_ - pos1_begin_, pos2_ - pos2_begin_); has_open_chunk_ = false; } } }; }; void Comparator::CalculateDifference(Comparator::Input* input, Comparator::Output* result_writer) { Differencer differencer(input); differencer.Initialize(); differencer.FillTable(); differencer.SaveResult(result_writer); } static bool CompareSubstrings(Handle s1, int pos1, Handle s2, int pos2, int len) { static StringInputBuffer buf1; static StringInputBuffer buf2; buf1.Reset(*s1); buf1.Seek(pos1); buf2.Reset(*s2); buf2.Seek(pos2); for (int i = 0; i < len; i++) { ASSERT(buf1.has_more() && buf2.has_more()); if (buf1.GetNext() != buf2.GetNext()) { return false; } } return true; } // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line // never has terminating new line character. class LineEndsWrapper { public: explicit LineEndsWrapper(Handle string) : ends_array_(CalculateLineEnds(string, false)), string_len_(string->length()) { } int length() { return ends_array_->length() + 1; } // Returns start for any line including start of the imaginary line after // the last line. int GetLineStart(int index) { if (index == 0) { return 0; } else { return GetLineEnd(index - 1); } } int GetLineEnd(int index) { if (index == ends_array_->length()) { // End of the last line is always an end of the whole string. // If the string ends with a new line character, the last line is an // empty string after this character. return string_len_; } else { return GetPosAfterNewLine(index); } } private: Handle ends_array_; int string_len_; int GetPosAfterNewLine(int index) { return Smi::cast(ends_array_->get(index))->value() + 1; } }; // Represents 2 strings as 2 arrays of lines. class LineArrayCompareInput : public Comparator::Input { public: LineArrayCompareInput(Handle s1, Handle s2, LineEndsWrapper line_ends1, LineEndsWrapper line_ends2) : s1_(s1), s2_(s2), line_ends1_(line_ends1), line_ends2_(line_ends2) { } int getLength1() { return line_ends1_.length(); } int getLength2() { return line_ends2_.length(); } bool equals(int index1, int index2) { int line_start1 = line_ends1_.GetLineStart(index1); int line_start2 = line_ends2_.GetLineStart(index2); int line_end1 = line_ends1_.GetLineEnd(index1); int line_end2 = line_ends2_.GetLineEnd(index2); int len1 = line_end1 - line_start1; int len2 = line_end2 - line_start2; if (len1 != len2) { return false; } return CompareSubstrings(s1_, line_start1, s2_, line_start2, len1); } private: Handle s1_; Handle s2_; LineEndsWrapper line_ends1_; LineEndsWrapper line_ends2_; }; // Stores compare result in JSArray. Each chunk is stored as 3 array elements: // (pos1_begin, pos1_end, pos2_end). class LineArrayCompareOutput : public Comparator::Output { public: LineArrayCompareOutput(LineEndsWrapper line_ends1, LineEndsWrapper line_ends2) : array_(Factory::NewJSArray(10)), current_size_(0), line_ends1_(line_ends1), line_ends2_(line_ends2) { } void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) { int char_pos1 = line_ends1_.GetLineStart(line_pos1); int char_pos2 = line_ends2_.GetLineStart(line_pos2); int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1; int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2; SetElement(array_, current_size_, Handle(Smi::FromInt(char_pos1))); SetElement(array_, current_size_ + 1, Handle(Smi::FromInt(char_pos1 + char_len1))); SetElement(array_, current_size_ + 2, Handle(Smi::FromInt(char_pos2 + char_len2))); current_size_ += 3; } Handle GetResult() { return array_; } private: Handle array_; int current_size_; LineEndsWrapper line_ends1_; LineEndsWrapper line_ends2_; }; Handle LiveEdit::CompareStringsLinewise(Handle s1, Handle s2) { LineEndsWrapper line_ends1(s1); LineEndsWrapper line_ends2(s2); LineArrayCompareInput input(s1, s2, line_ends1, line_ends2); LineArrayCompareOutput output(line_ends1, line_ends2); Comparator::CalculateDifference(&input, &output); return output.GetResult(); } static void CompileScriptForTracker(Handle