OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 24 matching lines...) Expand all Loading... |
35 #include "global-handles.h" | 35 #include "global-handles.h" |
36 #include "debug.h" | 36 #include "debug.h" |
37 #include "memory.h" | 37 #include "memory.h" |
38 | 38 |
39 namespace v8 { | 39 namespace v8 { |
40 namespace internal { | 40 namespace internal { |
41 | 41 |
42 | 42 |
43 #ifdef ENABLE_DEBUGGER_SUPPORT | 43 #ifdef ENABLE_DEBUGGER_SUPPORT |
44 | 44 |
| 45 |
| 46 // A simple implementation of dynamic programming algorithm. It solves |
| 47 // the problem of finding the difference of 2 arrays. It uses a table of results |
| 48 // of subproblems. Each cell contains a number together with 2-bit flag |
| 49 // that helps building the chunk list. |
| 50 class Differencer { |
| 51 public: |
| 52 explicit Differencer(Compare::Input* input) |
| 53 : input_(input), len1_(input->getLength1()), len2_(input->getLength2()) { |
| 54 buffer_ = NewArray<int>(len1_ * len2_); |
| 55 } |
| 56 ~Differencer() { |
| 57 DeleteArray(buffer_); |
| 58 } |
| 59 |
| 60 void Initialize() { |
| 61 int array_size = len1_ * len2_; |
| 62 for (int i = 0; i < array_size; i++) { |
| 63 buffer_[i] = kEmptyCellValue; |
| 64 } |
| 65 } |
| 66 |
| 67 // Makes sure that result for the full problem is calculated and stored |
| 68 // in the table together with flags showing a path through subproblems. |
| 69 void FillTable() { |
| 70 CompareUpToTail(0, 0); |
| 71 } |
| 72 |
| 73 void SaveResult(Compare::Output* chunk_writer) { |
| 74 ResultWriter writer(chunk_writer); |
| 75 |
| 76 int pos1 = 0; |
| 77 int pos2 = 0; |
| 78 while (true) { |
| 79 if (pos1 < len1_) { |
| 80 if (pos2 < len2_) { |
| 81 Direction dir = get_direction(pos1, pos2); |
| 82 switch (dir) { |
| 83 case EQ: |
| 84 writer.eq(); |
| 85 pos1++; |
| 86 pos2++; |
| 87 break; |
| 88 case SKIP1: |
| 89 writer.skip1(1); |
| 90 pos1++; |
| 91 break; |
| 92 case SKIP2: |
| 93 case SKIP_ANY: |
| 94 writer.skip2(1); |
| 95 pos2++; |
| 96 break; |
| 97 default: |
| 98 UNREACHABLE(); |
| 99 } |
| 100 } else { |
| 101 writer.skip1(len1_ - pos1); |
| 102 break; |
| 103 } |
| 104 } else { |
| 105 if (len2_ != pos2) { |
| 106 writer.skip2(len2_ - pos2); |
| 107 } |
| 108 break; |
| 109 } |
| 110 } |
| 111 writer.close(); |
| 112 } |
| 113 |
| 114 private: |
| 115 Compare::Input* input_; |
| 116 int* buffer_; |
| 117 int len1_; |
| 118 int len2_; |
| 119 |
| 120 enum Direction { |
| 121 EQ = 0, |
| 122 SKIP1, |
| 123 SKIP2, |
| 124 SKIP_ANY, |
| 125 |
| 126 MAX_DIRECTION_FLAG_VALUE = SKIP_ANY |
| 127 }; |
| 128 |
| 129 // Computes result for a subtask and optionally caches it in the buffer table. |
| 130 // All results values are shifted to make space for flags in the lower bits. |
| 131 int CompareUpToTail(int pos1, int pos2) { |
| 132 if (pos1 < len1_) { |
| 133 if (pos2 < len2_) { |
| 134 int cached_res = get_value4(pos1, pos2); |
| 135 if (cached_res == kEmptyCellValue) { |
| 136 Direction dir; |
| 137 int res; |
| 138 if (input_->equals(pos1, pos2)) { |
| 139 res = CompareUpToTail(pos1 + 1, pos2 + 1); |
| 140 dir = EQ; |
| 141 } else { |
| 142 int res1 = CompareUpToTail(pos1 + 1, pos2) + |
| 143 (1 << kDirectionSizeBits); |
| 144 int res2 = CompareUpToTail(pos1, pos2 + 1) + |
| 145 (1 << kDirectionSizeBits); |
| 146 if (res1 == res2) { |
| 147 res = res1; |
| 148 dir = SKIP_ANY; |
| 149 } else if (res1 < res2) { |
| 150 res = res1; |
| 151 dir = SKIP1; |
| 152 } else { |
| 153 res = res2; |
| 154 dir = SKIP2; |
| 155 } |
| 156 } |
| 157 set_value4_and_dir(pos1, pos2, res, dir); |
| 158 cached_res = res; |
| 159 } |
| 160 return cached_res; |
| 161 } else { |
| 162 return (len1_ - pos1) << kDirectionSizeBits; |
| 163 } |
| 164 } else { |
| 165 return (len2_ - pos2) << kDirectionSizeBits; |
| 166 } |
| 167 } |
| 168 |
| 169 inline int& get_cell(int i1, int i2) { |
| 170 return buffer_[i1 + i2 * len1_]; |
| 171 } |
| 172 |
| 173 // Each cell keeps a value plus direction. Value is multiplied by 4. |
| 174 void set_value4_and_dir(int i1, int i2, int value4, Direction dir) { |
| 175 ASSERT((value4 & kDirectionMask) == 0); |
| 176 get_cell(i1, i2) = value4 | dir; |
| 177 } |
| 178 |
| 179 int get_value4(int i1, int i2) { |
| 180 return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask); |
| 181 } |
| 182 Direction get_direction(int i1, int i2) { |
| 183 return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask); |
| 184 } |
| 185 |
| 186 static const int kDirectionSizeBits = 2; |
| 187 static const int kDirectionMask = (1 << kDirectionSizeBits) - 1; |
| 188 static const int kEmptyCellValue = -1 << kDirectionSizeBits; |
| 189 |
| 190 // This method only holds static assert statement (unfortunately you cannot |
| 191 // place one in class scope). |
| 192 void StaticAssertHolder() { |
| 193 STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits)); |
| 194 } |
| 195 |
| 196 class ResultWriter { |
| 197 public: |
| 198 explicit ResultWriter(Compare::Output* chunk_writer) |
| 199 : chunk_writer_(chunk_writer), pos1_(0), pos2_(0), |
| 200 pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) { |
| 201 } |
| 202 void eq() { |
| 203 FlushChunk(); |
| 204 pos1_++; |
| 205 pos2_++; |
| 206 } |
| 207 void skip1(int len1) { |
| 208 StartChunk(); |
| 209 pos1_ += len1; |
| 210 } |
| 211 void skip2(int len2) { |
| 212 StartChunk(); |
| 213 pos2_ += len2; |
| 214 } |
| 215 void close() { |
| 216 FlushChunk(); |
| 217 } |
| 218 |
| 219 private: |
| 220 Compare::Output* chunk_writer_; |
| 221 int pos1_; |
| 222 int pos2_; |
| 223 int pos1_begin_; |
| 224 int pos2_begin_; |
| 225 bool has_open_chunk_; |
| 226 |
| 227 void StartChunk() { |
| 228 if (!has_open_chunk_) { |
| 229 pos1_begin_ = pos1_; |
| 230 pos2_begin_ = pos2_; |
| 231 has_open_chunk_ = true; |
| 232 } |
| 233 } |
| 234 |
| 235 void FlushChunk() { |
| 236 if (has_open_chunk_) { |
| 237 chunk_writer_->AddChunk(pos1_begin_, pos2_begin_, |
| 238 pos1_ - pos1_begin_, pos2_ - pos2_begin_); |
| 239 has_open_chunk_ = false; |
| 240 } |
| 241 } |
| 242 }; |
| 243 }; |
| 244 |
| 245 |
| 246 void Compare::CalculateDifference(Compare::Input* input, |
| 247 Compare::Output* result_writer) { |
| 248 Differencer differencer(input); |
| 249 differencer.Initialize(); |
| 250 differencer.FillTable(); |
| 251 differencer.SaveResult(result_writer); |
| 252 } |
| 253 |
| 254 |
| 255 static bool CompareSubstrings(Handle<String> s1, int pos1, |
| 256 Handle<String> s2, int pos2, int len) { |
| 257 static StringInputBuffer buf1; |
| 258 static StringInputBuffer buf2; |
| 259 buf1.Reset(*s1); |
| 260 buf1.Seek(pos1); |
| 261 buf2.Reset(*s2); |
| 262 buf2.Seek(pos2); |
| 263 for (int i = 0; i < len; i++) { |
| 264 ASSERT(buf1.has_more() && buf2.has_more()); |
| 265 if (buf1.GetNext() != buf2.GetNext()) { |
| 266 return false; |
| 267 } |
| 268 } |
| 269 return true; |
| 270 } |
| 271 |
| 272 |
| 273 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line |
| 274 // never has terminating new line character. |
| 275 class LineEndsWrapper { |
| 276 public: |
| 277 explicit LineEndsWrapper(Handle<String> string) |
| 278 : ends_array_(CalculateLineEnds(string, false)), |
| 279 string_len_(string->length()) { |
| 280 } |
| 281 int length() { |
| 282 return ends_array_->length() + 1; |
| 283 } |
| 284 // Returns start for any line including start of the imaginary line after |
| 285 // the last line. |
| 286 int GetLineStart(int index) { |
| 287 if (index == 0) { |
| 288 return 0; |
| 289 } else { |
| 290 return GetLineEnd(index - 1); |
| 291 } |
| 292 } |
| 293 int GetLineEnd(int index) { |
| 294 if (index == ends_array_->length()) { |
| 295 // End of the last line is always an end of the whole string. |
| 296 // If the string ends with a new line character, the last line is an |
| 297 // empty string after this character. |
| 298 return string_len_; |
| 299 } else { |
| 300 return GetPosAfterNewLine(index); |
| 301 } |
| 302 } |
| 303 |
| 304 private: |
| 305 Handle<FixedArray> ends_array_; |
| 306 int string_len_; |
| 307 |
| 308 int GetPosAfterNewLine(int index) { |
| 309 return Smi::cast(ends_array_->get(index))->value() + 1; |
| 310 } |
| 311 }; |
| 312 |
| 313 |
| 314 // Represents 2 strings as 2 arrays of lines. |
| 315 class LineArrayCompareInput : public Compare::Input { |
| 316 public: |
| 317 LineArrayCompareInput(Handle<String> s1, Handle<String> s2, |
| 318 LineEndsWrapper line_ends1, LineEndsWrapper line_ends2) |
| 319 : s1_(s1), s2_(s2), line_ends1_(line_ends1), line_ends2_(line_ends2) { |
| 320 } |
| 321 int getLength1() { |
| 322 return line_ends1_.length(); |
| 323 } |
| 324 int getLength2() { |
| 325 return line_ends2_.length(); |
| 326 } |
| 327 bool equals(int index1, int index2) { |
| 328 int line_start1 = line_ends1_.GetLineStart(index1); |
| 329 int line_start2 = line_ends2_.GetLineStart(index2); |
| 330 int line_end1 = line_ends1_.GetLineEnd(index1); |
| 331 int line_end2 = line_ends2_.GetLineEnd(index2); |
| 332 int len1 = line_end1 - line_start1; |
| 333 int len2 = line_end2 - line_start2; |
| 334 if (len1 != len2) { |
| 335 return false; |
| 336 } |
| 337 return CompareSubstrings(s1_, line_start1, s2_, line_start2, len1); |
| 338 } |
| 339 |
| 340 private: |
| 341 Handle<String> s1_; |
| 342 Handle<String> s2_; |
| 343 LineEndsWrapper line_ends1_; |
| 344 LineEndsWrapper line_ends2_; |
| 345 }; |
| 346 |
| 347 |
| 348 // Stores compare result in JSArray. Each chunk is stored as 3 array elements: |
| 349 // (pos1, len1, len2). |
| 350 class LineArrayCompareOutput : public Compare::Output { |
| 351 public: |
| 352 LineArrayCompareOutput(LineEndsWrapper line_ends1, LineEndsWrapper line_ends2) |
| 353 : array_(Factory::NewJSArray(10)), current_size_(0), |
| 354 line_ends1_(line_ends1), line_ends2_(line_ends2) { |
| 355 } |
| 356 |
| 357 void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) { |
| 358 int char_pos1 = line_ends1_.GetLineStart(line_pos1); |
| 359 int char_pos2 = line_ends2_.GetLineStart(line_pos2); |
| 360 int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1; |
| 361 int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2; |
| 362 |
| 363 SetElement(array_, current_size_, Handle<Object>(Smi::FromInt(char_pos1))); |
| 364 SetElement(array_, current_size_ + 1, |
| 365 Handle<Object>(Smi::FromInt(char_len1))); |
| 366 SetElement(array_, current_size_ + 2, |
| 367 Handle<Object>(Smi::FromInt(char_len2))); |
| 368 current_size_ += 3; |
| 369 } |
| 370 |
| 371 Handle<JSArray> GetResult() { |
| 372 return array_; |
| 373 } |
| 374 |
| 375 private: |
| 376 Handle<JSArray> array_; |
| 377 int current_size_; |
| 378 LineEndsWrapper line_ends1_; |
| 379 LineEndsWrapper line_ends2_; |
| 380 }; |
| 381 |
| 382 |
| 383 Handle<JSArray> LiveEdit::CompareStringsLinewise(Handle<String> s1, |
| 384 Handle<String> s2) { |
| 385 LineEndsWrapper line_ends1(s1); |
| 386 LineEndsWrapper line_ends2(s2); |
| 387 |
| 388 LineArrayCompareInput input(s1, s2, line_ends1, line_ends2); |
| 389 LineArrayCompareOutput output(line_ends1, line_ends2); |
| 390 |
| 391 Compare::CalculateDifference(&input, &output); |
| 392 |
| 393 return output.GetResult(); |
| 394 } |
| 395 |
| 396 |
45 static void CompileScriptForTracker(Handle<Script> script) { | 397 static void CompileScriptForTracker(Handle<Script> script) { |
46 const bool is_eval = false; | 398 const bool is_eval = false; |
47 const bool is_global = true; | 399 const bool is_global = true; |
48 // TODO(635): support extensions. | 400 // TODO(635): support extensions. |
49 Extension* extension = NULL; | 401 Extension* extension = NULL; |
50 | 402 |
51 PostponeInterruptsScope postpone; | 403 PostponeInterruptsScope postpone; |
52 | 404 |
53 // Only allow non-global compiles for eval. | 405 // Only allow non-global compiles for eval. |
54 ASSERT(is_eval || is_global); | 406 ASSERT(is_eval || is_global); |
(...skipping 996 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1051 | 1403 |
1052 bool LiveEditFunctionTracker::IsActive() { | 1404 bool LiveEditFunctionTracker::IsActive() { |
1053 return false; | 1405 return false; |
1054 } | 1406 } |
1055 | 1407 |
1056 #endif // ENABLE_DEBUGGER_SUPPORT | 1408 #endif // ENABLE_DEBUGGER_SUPPORT |
1057 | 1409 |
1058 | 1410 |
1059 | 1411 |
1060 } } // namespace v8::internal | 1412 } } // namespace v8::internal |
OLD | NEW |