1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/debug/liveedit.h" 6 7 #include "src/api-inl.h" 8 #include "src/ast/ast-traversal-visitor.h" 9 #include "src/ast/ast.h" 10 #include "src/ast/scopes.h" 11 #include "src/compilation-cache.h" 12 #include "src/compiler.h" 13 #include "src/debug/debug-interface.h" 14 #include "src/debug/debug.h" 15 #include "src/frames-inl.h" 16 #include "src/isolate-inl.h" 17 #include "src/messages.h" 18 #include "src/objects-inl.h" 19 #include "src/objects/hash-table-inl.h" 20 #include "src/objects/js-generator-inl.h" 21 #include "src/parsing/parse-info.h" 22 #include "src/parsing/parsing.h" 23 #include "src/source-position-table.h" 24 #include "src/v8.h" 25 26 namespace v8 { 27 namespace internal { 28 namespace { 29 // A general-purpose comparator between 2 arrays. 30 class Comparator { 31 public: 32 // Holds 2 arrays of some elements allowing to compare any pair of 33 // element from the first array and element from the second array. 34 class Input { 35 public: 36 virtual int GetLength1() = 0; 37 virtual int GetLength2() = 0; 38 virtual bool Equals(int index1, int index2) = 0; 39 40 protected: 41 virtual ~Input() = default; 42 }; 43 44 // Receives compare result as a series of chunks. 45 class Output { 46 public: 47 // Puts another chunk in result list. Note that technically speaking 48 // only 3 arguments actually needed with 4th being derivable. 49 virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0; 50 51 protected: 52 virtual ~Output() = default; 53 }; 54 55 // Finds the difference between 2 arrays of elements. 56 static void CalculateDifference(Input* input, Output* result_writer); 57 }; 58 59 // A simple implementation of dynamic programming algorithm. It solves 60 // the problem of finding the difference of 2 arrays. It uses a table of results 61 // of subproblems. Each cell contains a number together with 2-bit flag 62 // that helps building the chunk list. 63 class Differencer { 64 public: 65 explicit Differencer(Comparator::Input* input) 66 : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) { 67 buffer_ = NewArray<int>(len1_ * len2_); 68 } 69 ~Differencer() { 70 DeleteArray(buffer_); 71 } 72 73 void Initialize() { 74 int array_size = len1_ * len2_; 75 for (int i = 0; i < array_size; i++) { 76 buffer_[i] = kEmptyCellValue; 77 } 78 } 79 80 // Makes sure that result for the full problem is calculated and stored 81 // in the table together with flags showing a path through subproblems. 82 void FillTable() { 83 CompareUpToTail(0, 0); 84 } 85 86 void SaveResult(Comparator::Output* chunk_writer) { 87 ResultWriter writer(chunk_writer); 88 89 int pos1 = 0; 90 int pos2 = 0; 91 while (true) { 92 if (pos1 < len1_) { 93 if (pos2 < len2_) { 94 Direction dir = get_direction(pos1, pos2); 95 switch (dir) { 96 case EQ: 97 writer.eq(); 98 pos1++; 99 pos2++; 100 break; 101 case SKIP1: 102 writer.skip1(1); 103 pos1++; 104 break; 105 case SKIP2: 106 case SKIP_ANY: 107 writer.skip2(1); 108 pos2++; 109 break; 110 default: 111 UNREACHABLE(); 112 } 113 } else { 114 writer.skip1(len1_ - pos1); 115 break; 116 } 117 } else { 118 if (len2_ != pos2) { 119 writer.skip2(len2_ - pos2); 120 } 121 break; 122 } 123 } 124 writer.close(); 125 } 126 127 private: 128 Comparator::Input* input_; 129 int* buffer_; 130 int len1_; 131 int len2_; 132 133 enum Direction { 134 EQ = 0, 135 SKIP1, 136 SKIP2, 137 SKIP_ANY, 138 139 MAX_DIRECTION_FLAG_VALUE = SKIP_ANY 140 }; 141 142 // Computes result for a subtask and optionally caches it in the buffer table. 143 // All results values are shifted to make space for flags in the lower bits. 144 int CompareUpToTail(int pos1, int pos2) { 145 if (pos1 < len1_) { 146 if (pos2 < len2_) { 147 int cached_res = get_value4(pos1, pos2); 148 if (cached_res == kEmptyCellValue) { 149 Direction dir; 150 int res; 151 if (input_->Equals(pos1, pos2)) { 152 res = CompareUpToTail(pos1 + 1, pos2 + 1); 153 dir = EQ; 154 } else { 155 int res1 = CompareUpToTail(pos1 + 1, pos2) + 156 (1 << kDirectionSizeBits); 157 int res2 = CompareUpToTail(pos1, pos2 + 1) + 158 (1 << kDirectionSizeBits); 159 if (res1 == res2) { 160 res = res1; 161 dir = SKIP_ANY; 162 } else if (res1 < res2) { 163 res = res1; 164 dir = SKIP1; 165 } else { 166 res = res2; 167 dir = SKIP2; 168 } 169 } 170 set_value4_and_dir(pos1, pos2, res, dir); 171 cached_res = res; 172 } 173 return cached_res; 174 } else { 175 return (len1_ - pos1) << kDirectionSizeBits; 176 } 177 } else { 178 return (len2_ - pos2) << kDirectionSizeBits; 179 } 180 } 181 182 inline int& get_cell(int i1, int i2) { 183 return buffer_[i1 + i2 * len1_]; 184 } 185 186 // Each cell keeps a value plus direction. Value is multiplied by 4. 187 void set_value4_and_dir(int i1, int i2, int value4, Direction dir) { 188 DCHECK_EQ(0, value4 & kDirectionMask); 189 get_cell(i1, i2) = value4 | dir; 190 } 191 192 int get_value4(int i1, int i2) { 193 return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask); 194 } 195 Direction get_direction(int i1, int i2) { 196 return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask); 197 } 198 199 static const int kDirectionSizeBits = 2; 200 static const int kDirectionMask = (1 << kDirectionSizeBits) - 1; 201 static const int kEmptyCellValue = ~0u << kDirectionSizeBits; 202 203 // This method only holds static assert statement (unfortunately you cannot 204 // place one in class scope). 205 void StaticAssertHolder() { 206 STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits)); 207 } 208 209 class ResultWriter { 210 public: 211 explicit ResultWriter(Comparator::Output* chunk_writer) 212 : chunk_writer_(chunk_writer), pos1_(0), pos2_(0), 213 pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) { 214 } 215 void eq() { 216 FlushChunk(); 217 pos1_++; 218 pos2_++; 219 } 220 void skip1(int len1) { 221 StartChunk(); 222 pos1_ += len1; 223 } 224 void skip2(int len2) { 225 StartChunk(); 226 pos2_ += len2; 227 } 228 void close() { 229 FlushChunk(); 230 } 231 232 private: 233 Comparator::Output* chunk_writer_; 234 int pos1_; 235 int pos2_; 236 int pos1_begin_; 237 int pos2_begin_; 238 bool has_open_chunk_; 239 240 void StartChunk() { 241 if (!has_open_chunk_) { 242 pos1_begin_ = pos1_; 243 pos2_begin_ = pos2_; 244 has_open_chunk_ = true; 245 } 246 } 247 248 void FlushChunk() { 249 if (has_open_chunk_) { 250 chunk_writer_->AddChunk(pos1_begin_, pos2_begin_, 251 pos1_ - pos1_begin_, pos2_ - pos2_begin_); 252 has_open_chunk_ = false; 253 } 254 } 255 }; 256 }; 257 258 void Comparator::CalculateDifference(Comparator::Input* input, 259 Comparator::Output* result_writer) { 260 Differencer differencer(input); 261 differencer.Initialize(); 262 differencer.FillTable(); 263 differencer.SaveResult(result_writer); 264 } 265 266 bool CompareSubstrings(Handle<String> s1, int pos1, Handle<String> s2, int pos2, 267 int len) { 268 for (int i = 0; i < len; i++) { 269 if (s1->Get(i + pos1) != s2->Get(i + pos2)) return false; 270 } 271 return true; 272 } 273 274 // Additional to Input interface. Lets switch Input range to subrange. 275 // More elegant way would be to wrap one Input as another Input object 276 // and translate positions there, but that would cost us additional virtual 277 // call per comparison. 278 class SubrangableInput : public Comparator::Input { 279 public: 280 virtual void SetSubrange1(int offset, int len) = 0; 281 virtual void SetSubrange2(int offset, int len) = 0; 282 }; 283 284 285 class SubrangableOutput : public Comparator::Output { 286 public: 287 virtual void SetSubrange1(int offset, int len) = 0; 288 virtual void SetSubrange2(int offset, int len) = 0; 289 }; 290 291 // Finds common prefix and suffix in input. This parts shouldn't take space in 292 // linear programming table. Enable subranging in input and output. 293 void NarrowDownInput(SubrangableInput* input, SubrangableOutput* output) { 294 const int len1 = input->GetLength1(); 295 const int len2 = input->GetLength2(); 296 297 int common_prefix_len; 298 int common_suffix_len; 299 300 { 301 common_prefix_len = 0; 302 int prefix_limit = std::min(len1, len2); 303 while (common_prefix_len < prefix_limit && 304 input->Equals(common_prefix_len, common_prefix_len)) { 305 common_prefix_len++; 306 } 307 308 common_suffix_len = 0; 309 int suffix_limit = 310 std::min(len1 - common_prefix_len, len2 - common_prefix_len); 311 312 while (common_suffix_len < suffix_limit && 313 input->Equals(len1 - common_suffix_len - 1, 314 len2 - common_suffix_len - 1)) { 315 common_suffix_len++; 316 } 317 } 318 319 if (common_prefix_len > 0 || common_suffix_len > 0) { 320 int new_len1 = len1 - common_suffix_len - common_prefix_len; 321 int new_len2 = len2 - common_suffix_len - common_prefix_len; 322 323 input->SetSubrange1(common_prefix_len, new_len1); 324 input->SetSubrange2(common_prefix_len, new_len2); 325 326 output->SetSubrange1(common_prefix_len, new_len1); 327 output->SetSubrange2(common_prefix_len, new_len2); 328 } 329 } 330 331 // Represents 2 strings as 2 arrays of tokens. 332 // TODO(LiveEdit): Currently it's actually an array of charactres. 333 // Make array of tokens instead. 334 class TokensCompareInput : public Comparator::Input { 335 public: 336 TokensCompareInput(Handle<String> s1, int offset1, int len1, 337 Handle<String> s2, int offset2, int len2) 338 : s1_(s1), offset1_(offset1), len1_(len1), 339 s2_(s2), offset2_(offset2), len2_(len2) { 340 } 341 int GetLength1() override { return len1_; } 342 int GetLength2() override { return len2_; } 343 bool Equals(int index1, int index2) override { 344 return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2); 345 } 346 347 private: 348 Handle<String> s1_; 349 int offset1_; 350 int len1_; 351 Handle<String> s2_; 352 int offset2_; 353 int len2_; 354 }; 355 356 // Stores compare result in std::vector. Converts substring positions 357 // to absolute positions. 358 class TokensCompareOutput : public Comparator::Output { 359 public: 360 TokensCompareOutput(int offset1, int offset2, 361 std::vector<SourceChangeRange>* output) 362 : output_(output), offset1_(offset1), offset2_(offset2) {} 363 364 void AddChunk(int pos1, int pos2, int len1, int len2) override { 365 output_->emplace_back( 366 SourceChangeRange{pos1 + offset1_, pos1 + len1 + offset1_, 367 pos2 + offset2_, pos2 + offset2_ + len2}); 368 } 369 370 private: 371 std::vector<SourceChangeRange>* output_; 372 int offset1_; 373 int offset2_; 374 }; 375 376 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line 377 // never has terminating new line character. 378 class LineEndsWrapper { 379 public: 380 explicit LineEndsWrapper(Isolate* isolate, Handle<String> string) 381 : ends_array_(String::CalculateLineEnds(isolate, string, false)), 382 string_len_(string->length()) {} 383 int length() { 384 return ends_array_->length() + 1; 385 } 386 // Returns start for any line including start of the imaginary line after 387 // the last line. 388 int GetLineStart(int index) { return index == 0 ? 0 : GetLineEnd(index - 1); } 389 int GetLineEnd(int index) { 390 if (index == ends_array_->length()) { 391 // End of the last line is always an end of the whole string. 392 // If the string ends with a new line character, the last line is an 393 // empty string after this character. 394 return string_len_; 395 } else { 396 return GetPosAfterNewLine(index); 397 } 398 } 399 400 private: 401 Handle<FixedArray> ends_array_; 402 int string_len_; 403 404 int GetPosAfterNewLine(int index) { 405 return Smi::ToInt(ends_array_->get(index)) + 1; 406 } 407 }; 408 409 // Represents 2 strings as 2 arrays of lines. 410 class LineArrayCompareInput : public SubrangableInput { 411 public: 412 LineArrayCompareInput(Handle<String> s1, Handle<String> s2, 413 LineEndsWrapper line_ends1, LineEndsWrapper line_ends2) 414 : s1_(s1), s2_(s2), line_ends1_(line_ends1), 415 line_ends2_(line_ends2), 416 subrange_offset1_(0), subrange_offset2_(0), 417 subrange_len1_(line_ends1_.length()), 418 subrange_len2_(line_ends2_.length()) { 419 } 420 int GetLength1() override { return subrange_len1_; } 421 int GetLength2() override { return subrange_len2_; } 422 bool Equals(int index1, int index2) override { 423 index1 += subrange_offset1_; 424 index2 += subrange_offset2_; 425 426 int line_start1 = line_ends1_.GetLineStart(index1); 427 int line_start2 = line_ends2_.GetLineStart(index2); 428 int line_end1 = line_ends1_.GetLineEnd(index1); 429 int line_end2 = line_ends2_.GetLineEnd(index2); 430 int len1 = line_end1 - line_start1; 431 int len2 = line_end2 - line_start2; 432 if (len1 != len2) { 433 return false; 434 } 435 return CompareSubstrings(s1_, line_start1, s2_, line_start2, 436 len1); 437 } 438 void SetSubrange1(int offset, int len) override { 439 subrange_offset1_ = offset; 440 subrange_len1_ = len; 441 } 442 void SetSubrange2(int offset, int len) override { 443 subrange_offset2_ = offset; 444 subrange_len2_ = len; 445 } 446 447 private: 448 Handle<String> s1_; 449 Handle<String> s2_; 450 LineEndsWrapper line_ends1_; 451 LineEndsWrapper line_ends2_; 452 int subrange_offset1_; 453 int subrange_offset2_; 454 int subrange_len1_; 455 int subrange_len2_; 456 }; 457 458 // Stores compare result in std::vector. For each chunk tries to conduct 459 // a fine-grained nested diff token-wise. 460 class TokenizingLineArrayCompareOutput : public SubrangableOutput { 461 public: 462 TokenizingLineArrayCompareOutput(Isolate* isolate, LineEndsWrapper line_ends1, 463 LineEndsWrapper line_ends2, 464 Handle<String> s1, Handle<String> s2, 465 std::vector<SourceChangeRange>* output) 466 : isolate_(isolate), 467 line_ends1_(line_ends1), 468 line_ends2_(line_ends2), 469 s1_(s1), 470 s2_(s2), 471 subrange_offset1_(0), 472 subrange_offset2_(0), 473 output_(output) {} 474 475 void AddChunk(int line_pos1, int line_pos2, int line_len1, 476 int line_len2) override { 477 line_pos1 += subrange_offset1_; 478 line_pos2 += subrange_offset2_; 479 480 int char_pos1 = line_ends1_.GetLineStart(line_pos1); 481 int char_pos2 = line_ends2_.GetLineStart(line_pos2); 482 int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1; 483 int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2; 484 485 if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) { 486 // Chunk is small enough to conduct a nested token-level diff. 487 HandleScope subTaskScope(isolate_); 488 489 TokensCompareInput tokens_input(s1_, char_pos1, char_len1, 490 s2_, char_pos2, char_len2); 491 TokensCompareOutput tokens_output(char_pos1, char_pos2, output_); 492 493 Comparator::CalculateDifference(&tokens_input, &tokens_output); 494 } else { 495 output_->emplace_back(SourceChangeRange{ 496 char_pos1, char_pos1 + char_len1, char_pos2, char_pos2 + char_len2}); 497 } 498 } 499 void SetSubrange1(int offset, int len) override { 500 subrange_offset1_ = offset; 501 } 502 void SetSubrange2(int offset, int len) override { 503 subrange_offset2_ = offset; 504 } 505 506 private: 507 static const int CHUNK_LEN_LIMIT = 800; 508 509 Isolate* isolate_; 510 LineEndsWrapper line_ends1_; 511 LineEndsWrapper line_ends2_; 512 Handle<String> s1_; 513 Handle<String> s2_; 514 int subrange_offset1_; 515 int subrange_offset2_; 516 std::vector<SourceChangeRange>* output_; 517 }; 518 519 struct SourcePositionEvent { 520 enum Type { LITERAL_STARTS, LITERAL_ENDS, DIFF_STARTS, DIFF_ENDS }; 521 522 int position; 523 Type type; 524 525 union { 526 FunctionLiteral* literal; 527 int pos_diff; 528 }; 529 530 SourcePositionEvent(FunctionLiteral* literal, bool is_start) 531 : position(is_start ? literal->start_position() 532 : literal->end_position()), 533 type(is_start ? LITERAL_STARTS : LITERAL_ENDS), 534 literal(literal) {} 535 SourcePositionEvent(const SourceChangeRange& change, bool is_start) 536 : position(is_start ? change.start_position : change.end_position), 537 type(is_start ? DIFF_STARTS : DIFF_ENDS), 538 pos_diff((change.new_end_position - change.new_start_position) - 539 (change.end_position - change.start_position)) {} 540 541 static bool LessThan(const SourcePositionEvent& a, 542 const SourcePositionEvent& b) { 543 if (a.position != b.position) return a.position < b.position; 544 if (a.type != b.type) return a.type < b.type; 545 if (a.type == LITERAL_STARTS && b.type == LITERAL_STARTS) { 546 // If the literals start in the same position, we want the one with the 547 // furthest (i.e. largest) end position to be first. 548 if (a.literal->end_position() != b.literal->end_position()) { 549 return a.literal->end_position() > b.literal->end_position(); 550 } 551 // If they also end in the same position, we want the first in order of 552 // literal ids to be first. 553 return a.literal->function_literal_id() < 554 b.literal->function_literal_id(); 555 } else if (a.type == LITERAL_ENDS && b.type == LITERAL_ENDS) { 556 // If the literals end in the same position, we want the one with the 557 // nearest (i.e. largest) start position to be first. 558 if (a.literal->start_position() != b.literal->start_position()) { 559 return a.literal->start_position() > b.literal->start_position(); 560 } 561 // If they also end in the same position, we want the last in order of 562 // literal ids to be first. 563 return a.literal->function_literal_id() > 564 b.literal->function_literal_id(); 565 } else { 566 return a.pos_diff < b.pos_diff; 567 } 568 } 569 }; 570 571 struct FunctionLiteralChange { 572 // If any of start/end position is kNoSourcePosition, this literal is 573 // considered damaged and will not be mapped and edited at all. 574 int new_start_position; 575 int new_end_position; 576 bool has_changes; 577 FunctionLiteral* outer_literal; 578 579 explicit FunctionLiteralChange(int new_start_position, FunctionLiteral* outer) 580 : new_start_position(new_start_position), 581 new_end_position(kNoSourcePosition), 582 has_changes(false), 583 outer_literal(outer) {} 584 }; 585 586 using FunctionLiteralChanges = 587 std::unordered_map<FunctionLiteral*, FunctionLiteralChange>; 588 void CalculateFunctionLiteralChanges( 589 const std::vector<FunctionLiteral*>& literals, 590 const std::vector<SourceChangeRange>& diffs, 591 FunctionLiteralChanges* result) { 592 std::vector<SourcePositionEvent> events; 593 events.reserve(literals.size() * 2 + diffs.size() * 2); 594 for (FunctionLiteral* literal : literals) { 595 events.emplace_back(literal, true); 596 events.emplace_back(literal, false); 597 } 598 for (const SourceChangeRange& diff : diffs) { 599 events.emplace_back(diff, true); 600 events.emplace_back(diff, false); 601 } 602 std::sort(events.begin(), events.end(), SourcePositionEvent::LessThan); 603 bool inside_diff = false; 604 int delta = 0; 605 std::stack<std::pair<FunctionLiteral*, FunctionLiteralChange>> literal_stack; 606 for (const SourcePositionEvent& event : events) { 607 switch (event.type) { 608 case SourcePositionEvent::DIFF_ENDS: 609 DCHECK(inside_diff); 610 inside_diff = false; 611 delta += event.pos_diff; 612 break; 613 case SourcePositionEvent::LITERAL_ENDS: { 614 DCHECK_EQ(literal_stack.top().first, event.literal); 615 FunctionLiteralChange& change = literal_stack.top().second; 616 change.new_end_position = inside_diff 617 ? kNoSourcePosition 618 : event.literal->end_position() + delta; 619 result->insert(literal_stack.top()); 620 literal_stack.pop(); 621 break; 622 } 623 case SourcePositionEvent::LITERAL_STARTS: 624 literal_stack.push(std::make_pair( 625 event.literal, 626 FunctionLiteralChange( 627 inside_diff ? kNoSourcePosition 628 : event.literal->start_position() + delta, 629 literal_stack.empty() ? nullptr : literal_stack.top().first))); 630 break; 631 case SourcePositionEvent::DIFF_STARTS: 632 DCHECK(!inside_diff); 633 inside_diff = true; 634 if (!literal_stack.empty()) { 635 // Note that outer literal has not necessarily changed, unless the 636 // diff goes past the end of this literal. In this case, we'll mark 637 // this function as damaged and parent as changed later in 638 // MapLiterals. 639 literal_stack.top().second.has_changes = true; 640 } 641 break; 642 } 643 } 644 } 645 646 // Function which has not changed itself, but if any variable in its 647 // outer context has been added/removed, we must consider this function 648 // as damaged and not update references to it. 649 // This is because old compiled function has hardcoded references to 650 // it's outer context. 651 bool HasChangedScope(FunctionLiteral* a, FunctionLiteral* b) { 652 Scope* scope_a = a->scope()->outer_scope(); 653 Scope* scope_b = b->scope()->outer_scope(); 654 while (scope_a && scope_b) { 655 std::unordered_map<int, Handle<String>> vars; 656 for (Variable* var : *scope_a->locals()) { 657 if (!var->IsContextSlot()) continue; 658 vars[var->index()] = var->name(); 659 } 660 for (Variable* var : *scope_b->locals()) { 661 if (!var->IsContextSlot()) continue; 662 auto it = vars.find(var->index()); 663 if (it == vars.end()) return true; 664 if (*it->second != *var->name()) return true; 665 } 666 scope_a = scope_a->outer_scope(); 667 scope_b = scope_b->outer_scope(); 668 } 669 return scope_a != scope_b; 670 } 671 672 enum ChangeState { UNCHANGED, CHANGED, DAMAGED }; 673 674 using LiteralMap = std::unordered_map<FunctionLiteral*, FunctionLiteral*>; 675 void MapLiterals(const FunctionLiteralChanges& changes, 676 const std::vector<FunctionLiteral*>& new_literals, 677 LiteralMap* unchanged, LiteralMap* changed) { 678 // Track the top-level script function separately as it can overlap fully with 679 // another function, e.g. the script "()=>42". 680 const std::pair<int, int> kTopLevelMarker = std::make_pair(-1, -1); 681 std::map<std::pair<int, int>, FunctionLiteral*> position_to_new_literal; 682 for (FunctionLiteral* literal : new_literals) { 683 DCHECK(literal->start_position() != kNoSourcePosition); 684 DCHECK(literal->end_position() != kNoSourcePosition); 685 std::pair<int, int> key = 686 literal->function_literal_id() == FunctionLiteral::kIdTypeTopLevel 687 ? kTopLevelMarker 688 : std::make_pair(literal->start_position(), 689 literal->end_position()); 690 // Make sure there are no duplicate keys. 691 DCHECK_EQ(position_to_new_literal.find(key), position_to_new_literal.end()); 692 position_to_new_literal[key] = literal; 693 } 694 LiteralMap mappings; 695 std::unordered_map<FunctionLiteral*, ChangeState> change_state; 696 for (const auto& change_pair : changes) { 697 FunctionLiteral* literal = change_pair.first; 698 const FunctionLiteralChange& change = change_pair.second; 699 std::pair<int, int> key = 700 literal->function_literal_id() == FunctionLiteral::kIdTypeTopLevel 701 ? kTopLevelMarker 702 : std::make_pair(change.new_start_position, 703 change.new_end_position); 704 auto it = position_to_new_literal.find(key); 705 if (it == position_to_new_literal.end() || 706 HasChangedScope(literal, it->second)) { 707 change_state[literal] = ChangeState::DAMAGED; 708 if (!change.outer_literal) continue; 709 if (change_state[change.outer_literal] != ChangeState::DAMAGED) { 710 change_state[change.outer_literal] = ChangeState::CHANGED; 711 } 712 } else { 713 mappings[literal] = it->second; 714 if (change_state.find(literal) == change_state.end()) { 715 change_state[literal] = 716 change.has_changes ? ChangeState::CHANGED : ChangeState::UNCHANGED; 717 } 718 } 719 } 720 for (const auto& mapping : mappings) { 721 if (change_state[mapping.first] == ChangeState::UNCHANGED) { 722 (*unchanged)[mapping.first] = mapping.second; 723 } else if (change_state[mapping.first] == ChangeState::CHANGED) { 724 (*changed)[mapping.first] = mapping.second; 725 } 726 } 727 } 728 729 class CollectFunctionLiterals final 730 : public AstTraversalVisitor<CollectFunctionLiterals> { 731 public: 732 CollectFunctionLiterals(Isolate* isolate, AstNode* root) 733 : AstTraversalVisitor<CollectFunctionLiterals>(isolate, root) {} 734 void VisitFunctionLiteral(FunctionLiteral* lit) { 735 AstTraversalVisitor::VisitFunctionLiteral(lit); 736 literals_->push_back(lit); 737 } 738 void Run(std::vector<FunctionLiteral*>* literals) { 739 literals_ = literals; 740 AstTraversalVisitor::Run(); 741 literals_ = nullptr; 742 } 743 744 private: 745 std::vector<FunctionLiteral*>* literals_; 746 }; 747 748 bool ParseScript(Isolate* isolate, ParseInfo* parse_info, bool compile_as_well, 749 std::vector<FunctionLiteral*>* literals, 750 debug::LiveEditResult* result) { 751 parse_info->set_eager(); 752 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); 753 Handle<SharedFunctionInfo> shared; 754 bool success = false; 755 if (compile_as_well) { 756 success = 757 Compiler::CompileForLiveEdit(parse_info, isolate).ToHandle(&shared); 758 } else { 759 success = parsing::ParseProgram(parse_info, isolate); 760 if (success) { 761 success = Compiler::Analyze(parse_info); 762 parse_info->ast_value_factory()->Internalize(isolate); 763 } 764 } 765 if (!success) { 766 isolate->OptionalRescheduleException(false); 767 DCHECK(try_catch.HasCaught()); 768 result->message = try_catch.Message()->Get(); 769 auto self = Utils::OpenHandle(*try_catch.Message()); 770 auto msg = i::Handle<i::JSMessageObject>::cast(self); 771 result->line_number = msg->GetLineNumber(); 772 result->column_number = msg->GetColumnNumber(); 773 result->status = debug::LiveEditResult::COMPILE_ERROR; 774 return false; 775 } 776 CollectFunctionLiterals(isolate, parse_info->literal()).Run(literals); 777 return true; 778 } 779 780 struct FunctionData { 781 FunctionData(FunctionLiteral* literal, bool should_restart) 782 : literal(literal), 783 stack_position(NOT_ON_STACK), 784 should_restart(should_restart) {} 785 786 FunctionLiteral* literal; 787 MaybeHandle<SharedFunctionInfo> shared; 788 std::vector<Handle<JSFunction>> js_functions; 789 std::vector<Handle<JSGeneratorObject>> running_generators; 790 // In case of multiple functions with different stack position, the latest 791 // one (in the order below) is used, since it is the most restrictive. 792 // This is important only for functions to be restarted. 793 enum StackPosition { 794 NOT_ON_STACK, 795 ABOVE_BREAK_FRAME, 796 PATCHABLE, 797 BELOW_NON_DROPPABLE_FRAME, 798 ARCHIVED_THREAD, 799 }; 800 StackPosition stack_position; 801 bool should_restart; 802 }; 803 804 class FunctionDataMap : public ThreadVisitor { 805 public: 806 void AddInterestingLiteral(int script_id, FunctionLiteral* literal, 807 bool should_restart) { 808 map_.emplace(GetFuncId(script_id, literal), 809 FunctionData{literal, should_restart}); 810 } 811 812 bool Lookup(SharedFunctionInfo* sfi, FunctionData** data) { 813 int start_position = sfi->StartPosition(); 814 if (!sfi->script()->IsScript() || start_position == -1) { 815 return false; 816 } 817 Script* script = Script::cast(sfi->script()); 818 return Lookup(GetFuncId(script->id(), sfi), data); 819 } 820 821 bool Lookup(Handle<Script> script, FunctionLiteral* literal, 822 FunctionData** data) { 823 return Lookup(GetFuncId(script->id(), literal), data); 824 } 825 826 void Fill(Isolate* isolate, Address* restart_frame_fp) { 827 { 828 HeapIterator iterator(isolate->heap(), HeapIterator::kFilterUnreachable); 829 while (HeapObject* obj = iterator.next()) { 830 if (obj->IsSharedFunctionInfo()) { 831 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); 832 FunctionData* data = nullptr; 833 if (!Lookup(sfi, &data)) continue; 834 data->shared = handle(sfi, isolate); 835 } else if (obj->IsJSFunction()) { 836 JSFunction* js_function = JSFunction::cast(obj); 837 SharedFunctionInfo* sfi = js_function->shared(); 838 FunctionData* data = nullptr; 839 if (!Lookup(sfi, &data)) continue; 840 data->js_functions.emplace_back(js_function, isolate); 841 } else if (obj->IsJSGeneratorObject()) { 842 JSGeneratorObject* gen = JSGeneratorObject::cast(obj); 843 if (gen->is_closed()) continue; 844 SharedFunctionInfo* sfi = gen->function()->shared(); 845 FunctionData* data = nullptr; 846 if (!Lookup(sfi, &data)) continue; 847 data->running_generators.emplace_back(gen, isolate); 848 } 849 } 850 } 851 FunctionData::StackPosition stack_position = 852 isolate->debug()->break_frame_id() == StackFrame::NO_ID 853 ? FunctionData::PATCHABLE 854 : FunctionData::ABOVE_BREAK_FRAME; 855 for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { 856 StackFrame* frame = it.frame(); 857 if (stack_position == FunctionData::ABOVE_BREAK_FRAME) { 858 if (frame->id() == isolate->debug()->break_frame_id()) { 859 stack_position = FunctionData::PATCHABLE; 860 } 861 } 862 if (stack_position == FunctionData::PATCHABLE && 863 (frame->is_exit() || frame->is_builtin_exit())) { 864 stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME; 865 continue; 866 } 867 if (!frame->is_java_script()) continue; 868 std::vector<Handle<SharedFunctionInfo>> sfis; 869 JavaScriptFrame::cast(frame)->GetFunctions(&sfis); 870 for (auto& sfi : sfis) { 871 if (stack_position == FunctionData::PATCHABLE && 872 IsResumableFunction(sfi->kind())) { 873 stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME; 874 } 875 FunctionData* data = nullptr; 876 if (!Lookup(*sfi, &data)) continue; 877 if (!data->should_restart) continue; 878 data->stack_position = stack_position; 879 *restart_frame_fp = frame->fp(); 880 } 881 } 882 883 isolate->thread_manager()->IterateArchivedThreads(this); 884 } 885 886 private: 887 // Unique id for a function: script_id + start_position, where start_position 888 // is special cased to -1 for top-level so that it does not overlap with a 889 // function whose start position is 0. 890 using FuncId = std::pair<int, int>; 891 892 FuncId GetFuncId(int script_id, FunctionLiteral* literal) { 893 int start_position = literal->start_position(); 894 if (literal->function_literal_id() == 0) { 895 // This is the top-level script function literal, so special case its 896 // start position 897 DCHECK_EQ(start_position, 0); 898 start_position = -1; 899 } 900 return FuncId(script_id, start_position); 901 } 902 903 FuncId GetFuncId(int script_id, SharedFunctionInfo* sfi) { 904 DCHECK_EQ(script_id, Script::cast(sfi->script())->id()); 905 int start_position = sfi->StartPosition(); 906 DCHECK_NE(start_position, -1); 907 if (sfi->is_toplevel()) { 908 // This is the top-level function, so special case its start position 909 DCHECK_EQ(start_position, 0); 910 start_position = -1; 911 } 912 return FuncId(script_id, start_position); 913 } 914 915 bool Lookup(FuncId id, FunctionData** data) { 916 auto it = map_.find(id); 917 if (it == map_.end()) return false; 918 *data = &it->second; 919 return true; 920 } 921 922 void VisitThread(Isolate* isolate, ThreadLocalTop* top) override { 923 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { 924 std::vector<Handle<SharedFunctionInfo>> sfis; 925 it.frame()->GetFunctions(&sfis); 926 for (auto& sfi : sfis) { 927 FunctionData* data = nullptr; 928 if (!Lookup(*sfi, &data)) continue; 929 data->stack_position = FunctionData::ARCHIVED_THREAD; 930 } 931 } 932 } 933 934 std::map<FuncId, FunctionData> map_; 935 }; 936 937 bool CanPatchScript(const LiteralMap& changed, Handle<Script> script, 938 Handle<Script> new_script, 939 FunctionDataMap& function_data_map, 940 debug::LiveEditResult* result) { 941 debug::LiveEditResult::Status status = debug::LiveEditResult::OK; 942 for (const auto& mapping : changed) { 943 FunctionData* data = nullptr; 944 function_data_map.Lookup(script, mapping.first, &data); 945 FunctionData* new_data = nullptr; 946 function_data_map.Lookup(new_script, mapping.second, &new_data); 947 Handle<SharedFunctionInfo> sfi; 948 if (!data->shared.ToHandle(&sfi)) { 949 continue; 950 } else if (!data->should_restart) { 951 UNREACHABLE(); 952 } else if (data->stack_position == FunctionData::ABOVE_BREAK_FRAME) { 953 status = debug::LiveEditResult::BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME; 954 } else if (data->stack_position == 955 FunctionData::BELOW_NON_DROPPABLE_FRAME) { 956 status = 957 debug::LiveEditResult::BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME; 958 } else if (!data->running_generators.empty()) { 959 status = debug::LiveEditResult::BLOCKED_BY_RUNNING_GENERATOR; 960 } else if (data->stack_position == FunctionData::ARCHIVED_THREAD) { 961 status = debug::LiveEditResult::BLOCKED_BY_ACTIVE_FUNCTION; 962 } 963 if (status != debug::LiveEditResult::OK) { 964 result->status = status; 965 return false; 966 } 967 } 968 return true; 969 } 970 971 bool CanRestartFrame(Isolate* isolate, Address fp, 972 FunctionDataMap& function_data_map, 973 const LiteralMap& changed, debug::LiveEditResult* result) { 974 DCHECK_GT(fp, 0); 975 StackFrame* restart_frame = nullptr; 976 StackFrameIterator it(isolate); 977 for (; !it.done(); it.Advance()) { 978 if (it.frame()->fp() == fp) { 979 restart_frame = it.frame(); 980 break; 981 } 982 } 983 DCHECK(restart_frame && restart_frame->is_java_script()); 984 if (!LiveEdit::kFrameDropperSupported) { 985 result->status = debug::LiveEditResult::FRAME_RESTART_IS_NOT_SUPPORTED; 986 return false; 987 } 988 std::vector<Handle<SharedFunctionInfo>> sfis; 989 JavaScriptFrame::cast(restart_frame)->GetFunctions(&sfis); 990 for (auto& sfi : sfis) { 991 FunctionData* data = nullptr; 992 if (!function_data_map.Lookup(*sfi, &data)) continue; 993 auto new_literal_it = changed.find(data->literal); 994 if (new_literal_it == changed.end()) continue; 995 if (new_literal_it->second->scope()->new_target_var()) { 996 result->status = 997 debug::LiveEditResult::BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME; 998 return false; 999 } 1000 } 1001 return true; 1002 } 1003 1004 void TranslateSourcePositionTable(Isolate* isolate, Handle<BytecodeArray> code, 1005 const std::vector<SourceChangeRange>& diffs) { 1006 SourcePositionTableBuilder builder; 1007 1008 Handle<ByteArray> source_position_table(code->SourcePositionTable(), isolate); 1009 for (SourcePositionTableIterator iterator(*source_position_table); 1010 !iterator.done(); iterator.Advance()) { 1011 SourcePosition position = iterator.source_position(); 1012 position.SetScriptOffset( 1013 LiveEdit::TranslatePosition(diffs, position.ScriptOffset())); 1014 builder.AddPosition(iterator.code_offset(), position, 1015 iterator.is_statement()); 1016 } 1017 1018 Handle<ByteArray> new_source_position_table( 1019 builder.ToSourcePositionTable(isolate)); 1020 code->set_source_position_table(*new_source_position_table); 1021 LOG_CODE_EVENT(isolate, 1022 CodeLinePosInfoRecordEvent(code->GetFirstBytecodeAddress(), 1023 *new_source_position_table)); 1024 } 1025 1026 void UpdatePositions(Isolate* isolate, Handle<SharedFunctionInfo> sfi, 1027 const std::vector<SourceChangeRange>& diffs) { 1028 int old_start_position = sfi->StartPosition(); 1029 int new_start_position = 1030 LiveEdit::TranslatePosition(diffs, old_start_position); 1031 int new_end_position = LiveEdit::TranslatePosition(diffs, sfi->EndPosition()); 1032 int new_function_token_position = 1033 LiveEdit::TranslatePosition(diffs, sfi->function_token_position()); 1034 sfi->SetPosition(new_start_position, new_end_position); 1035 sfi->SetFunctionTokenPosition(new_function_token_position, 1036 new_start_position); 1037 if (sfi->HasBytecodeArray()) { 1038 TranslateSourcePositionTable( 1039 isolate, handle(sfi->GetBytecodeArray(), isolate), diffs); 1040 } 1041 } 1042 } // anonymous namespace 1043 1044 void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script, 1045 Handle<String> new_source, bool preview, 1046 debug::LiveEditResult* result) { 1047 std::vector<SourceChangeRange> diffs; 1048 LiveEdit::CompareStrings(isolate, 1049 handle(String::cast(script->source()), isolate), 1050 new_source, &diffs); 1051 if (diffs.empty()) { 1052 result->status = debug::LiveEditResult::OK; 1053 return; 1054 } 1055 1056 ParseInfo parse_info(isolate, script); 1057 std::vector<FunctionLiteral*> literals; 1058 if (!ParseScript(isolate, &parse_info, false, &literals, result)) return; 1059 1060 Handle<Script> new_script = isolate->factory()->CloneScript(script); 1061 new_script->set_source(*new_source); 1062 std::vector<FunctionLiteral*> new_literals; 1063 ParseInfo new_parse_info(isolate, new_script); 1064 if (!ParseScript(isolate, &new_parse_info, true, &new_literals, result)) { 1065 return; 1066 } 1067 1068 FunctionLiteralChanges literal_changes; 1069 CalculateFunctionLiteralChanges(literals, diffs, &literal_changes); 1070 1071 LiteralMap changed; 1072 LiteralMap unchanged; 1073 MapLiterals(literal_changes, new_literals, &unchanged, &changed); 1074 1075 FunctionDataMap function_data_map; 1076 for (const auto& mapping : changed) { 1077 function_data_map.AddInterestingLiteral(script->id(), mapping.first, true); 1078 function_data_map.AddInterestingLiteral(new_script->id(), mapping.second, 1079 false); 1080 } 1081 for (const auto& mapping : unchanged) { 1082 function_data_map.AddInterestingLiteral(script->id(), mapping.first, false); 1083 } 1084 Address restart_frame_fp = 0; 1085 function_data_map.Fill(isolate, &restart_frame_fp); 1086 1087 if (!CanPatchScript(changed, script, new_script, function_data_map, result)) { 1088 return; 1089 } 1090 if (restart_frame_fp && 1091 !CanRestartFrame(isolate, restart_frame_fp, function_data_map, changed, 1092 result)) { 1093 return; 1094 } 1095 1096 if (preview) { 1097 result->status = debug::LiveEditResult::OK; 1098 return; 1099 } 1100 1101 std::map<int, int> start_position_to_unchanged_id; 1102 for (const auto& mapping : unchanged) { 1103 FunctionData* data = nullptr; 1104 if (!function_data_map.Lookup(script, mapping.first, &data)) continue; 1105 Handle<SharedFunctionInfo> sfi; 1106 if (!data->shared.ToHandle(&sfi)) continue; 1107 DCHECK_EQ(sfi->script(), *script); 1108 1109 isolate->compilation_cache()->Remove(sfi); 1110 isolate->debug()->DeoptimizeFunction(sfi); 1111 if (sfi->HasDebugInfo()) { 1112 Handle<DebugInfo> debug_info(sfi->GetDebugInfo(), isolate); 1113 isolate->debug()->RemoveBreakInfoAndMaybeFree(debug_info); 1114 } 1115 UpdatePositions(isolate, sfi, diffs); 1116 1117 sfi->set_script(*new_script); 1118 if (sfi->HasUncompiledData()) { 1119 sfi->uncompiled_data()->set_function_literal_id( 1120 mapping.second->function_literal_id()); 1121 } 1122 new_script->shared_function_infos()->Set( 1123 mapping.second->function_literal_id(), HeapObjectReference::Weak(*sfi)); 1124 DCHECK_EQ(sfi->FunctionLiteralId(isolate), 1125 mapping.second->function_literal_id()); 1126 1127 // Save the new start_position -> id mapping, so that we can recover it when 1128 // iterating over changed functions' constant pools. 1129 start_position_to_unchanged_id[mapping.second->start_position()] = 1130 mapping.second->function_literal_id(); 1131 1132 if (sfi->HasUncompiledDataWithPreParsedScope()) { 1133 sfi->ClearPreParsedScopeData(); 1134 } 1135 1136 for (auto& js_function : data->js_functions) { 1137 js_function->set_feedback_cell(*isolate->factory()->many_closures_cell()); 1138 if (!js_function->is_compiled()) continue; 1139 JSFunction::EnsureFeedbackVector(js_function); 1140 } 1141 1142 if (!sfi->HasBytecodeArray()) continue; 1143 FixedArray* constants = sfi->GetBytecodeArray()->constant_pool(); 1144 for (int i = 0; i < constants->length(); ++i) { 1145 if (!constants->get(i)->IsSharedFunctionInfo()) continue; 1146 FunctionData* data = nullptr; 1147 if (!function_data_map.Lookup(SharedFunctionInfo::cast(constants->get(i)), 1148 &data)) { 1149 continue; 1150 } 1151 auto change_it = changed.find(data->literal); 1152 if (change_it == changed.end()) continue; 1153 if (!function_data_map.Lookup(new_script, change_it->second, &data)) { 1154 continue; 1155 } 1156 Handle<SharedFunctionInfo> new_sfi; 1157 if (!data->shared.ToHandle(&new_sfi)) continue; 1158 constants->set(i, *new_sfi); 1159 } 1160 } 1161 for (const auto& mapping : changed) { 1162 FunctionData* data = nullptr; 1163 if (!function_data_map.Lookup(new_script, mapping.second, &data)) continue; 1164 Handle<SharedFunctionInfo> new_sfi = data->shared.ToHandleChecked(); 1165 DCHECK_EQ(new_sfi->script(), *new_script); 1166 1167 if (!function_data_map.Lookup(script, mapping.first, &data)) continue; 1168 Handle<SharedFunctionInfo> sfi; 1169 if (!data->shared.ToHandle(&sfi)) continue; 1170 1171 isolate->debug()->DeoptimizeFunction(sfi); 1172 isolate->compilation_cache()->Remove(sfi); 1173 for (auto& js_function : data->js_functions) { 1174 js_function->set_shared(*new_sfi); 1175 js_function->set_code(js_function->shared()->GetCode()); 1176 1177 js_function->set_feedback_cell(*isolate->factory()->many_closures_cell()); 1178 if (!js_function->is_compiled()) continue; 1179 JSFunction::EnsureFeedbackVector(js_function); 1180 } 1181 1182 if (!new_sfi->HasBytecodeArray()) continue; 1183 FixedArray* constants = new_sfi->GetBytecodeArray()->constant_pool(); 1184 for (int i = 0; i < constants->length(); ++i) { 1185 if (!constants->get(i)->IsSharedFunctionInfo()) continue; 1186 SharedFunctionInfo* inner_sfi = 1187 SharedFunctionInfo::cast(constants->get(i)); 1188 1189 // See if there is a mapping from this function's start position to a 1190 // unchanged function's id. 1191 auto unchanged_it = 1192 start_position_to_unchanged_id.find(inner_sfi->StartPosition()); 1193 if (unchanged_it == start_position_to_unchanged_id.end()) continue; 1194 1195 // Grab that function id from the new script's SFI list, which should have 1196 // already been updated in in the unchanged pass. 1197 SharedFunctionInfo* old_unchanged_inner_sfi = 1198 SharedFunctionInfo::cast(new_script->shared_function_infos() 1199 ->Get(unchanged_it->second) 1200 ->GetHeapObject()); 1201 // Now some sanity checks. Make sure that this inner_sfi is not the 1202 // unchanged SFI yet... 1203 DCHECK_NE(old_unchanged_inner_sfi, inner_sfi); 1204 // ... and that the unchanged SFI has already been processed and patched 1205 // to be on the new script ... 1206 DCHECK_EQ(old_unchanged_inner_sfi->script(), *new_script); 1207 1208 constants->set(i, old_unchanged_inner_sfi); 1209 } 1210 } 1211 #ifdef DEBUG 1212 { 1213 // Check that all the functions in the new script are valid, that their 1214 // function literals match what is expected, and that start positions are 1215 // unique. 1216 DisallowHeapAllocation no_gc; 1217 1218 SharedFunctionInfo::ScriptIterator it(isolate, *new_script); 1219 std::set<int> start_positions; 1220 while (SharedFunctionInfo* sfi = it.Next()) { 1221 DCHECK_EQ(sfi->script(), *new_script); 1222 DCHECK_EQ(sfi->FunctionLiteralId(isolate), it.CurrentIndex()); 1223 // Don't check the start position of the top-level function, as it can 1224 // overlap with a function in the script. 1225 if (sfi->is_toplevel()) { 1226 DCHECK_EQ(start_positions.find(sfi->StartPosition()), 1227 start_positions.end()); 1228 start_positions.insert(sfi->StartPosition()); 1229 } 1230 1231 if (!sfi->HasBytecodeArray()) continue; 1232 // Check that all the functions in this function's constant pool are also 1233 // on the new script, and that their id matches their index in the new 1234 // scripts function list. 1235 FixedArray* constants = sfi->GetBytecodeArray()->constant_pool(); 1236 for (int i = 0; i < constants->length(); ++i) { 1237 if (!constants->get(i)->IsSharedFunctionInfo()) continue; 1238 SharedFunctionInfo* inner_sfi = 1239 SharedFunctionInfo::cast(constants->get(i)); 1240 DCHECK_EQ(inner_sfi->script(), *new_script); 1241 DCHECK_EQ(inner_sfi, new_script->shared_function_infos() 1242 ->Get(inner_sfi->FunctionLiteralId(isolate)) 1243 ->GetHeapObject()); 1244 } 1245 } 1246 } 1247 #endif 1248 1249 if (restart_frame_fp) { 1250 for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { 1251 if (it.frame()->fp() == restart_frame_fp) { 1252 isolate->debug()->ScheduleFrameRestart(it.frame()); 1253 result->stack_changed = true; 1254 break; 1255 } 1256 } 1257 } 1258 1259 int script_id = script->id(); 1260 script->set_id(new_script->id()); 1261 new_script->set_id(script_id); 1262 result->status = debug::LiveEditResult::OK; 1263 result->script = ToApiHandle<v8::debug::Script>(new_script); 1264 } 1265 1266 void LiveEdit::InitializeThreadLocal(Debug* debug) { 1267 debug->thread_local_.restart_fp_ = 0; 1268 } 1269 1270 bool LiveEdit::RestartFrame(JavaScriptFrame* frame) { 1271 if (!LiveEdit::kFrameDropperSupported) return false; 1272 Isolate* isolate = frame->isolate(); 1273 StackFrame::Id break_frame_id = isolate->debug()->break_frame_id(); 1274 bool break_frame_found = break_frame_id == StackFrame::NO_ID; 1275 for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { 1276 StackFrame* current = it.frame(); 1277 break_frame_found = break_frame_found || break_frame_id == current->id(); 1278 if (current->fp() == frame->fp()) { 1279 if (break_frame_found) { 1280 isolate->debug()->ScheduleFrameRestart(current); 1281 return true; 1282 } else { 1283 return false; 1284 } 1285 } 1286 if (!break_frame_found) continue; 1287 if (current->is_exit() || current->is_builtin_exit()) { 1288 return false; 1289 } 1290 if (!current->is_java_script()) continue; 1291 std::vector<Handle<SharedFunctionInfo>> shareds; 1292 JavaScriptFrame::cast(current)->GetFunctions(&shareds); 1293 for (auto& shared : shareds) { 1294 if (IsResumableFunction(shared->kind())) { 1295 return false; 1296 } 1297 } 1298 } 1299 return false; 1300 } 1301 1302 void LiveEdit::CompareStrings(Isolate* isolate, Handle<String> s1, 1303 Handle<String> s2, 1304 std::vector<SourceChangeRange>* diffs) { 1305 s1 = String::Flatten(isolate, s1); 1306 s2 = String::Flatten(isolate, s2); 1307 1308 LineEndsWrapper line_ends1(isolate, s1); 1309 LineEndsWrapper line_ends2(isolate, s2); 1310 1311 LineArrayCompareInput input(s1, s2, line_ends1, line_ends2); 1312 TokenizingLineArrayCompareOutput output(isolate, line_ends1, line_ends2, s1, 1313 s2, diffs); 1314 1315 NarrowDownInput(&input, &output); 1316 1317 Comparator::CalculateDifference(&input, &output); 1318 } 1319 1320 int LiveEdit::TranslatePosition(const std::vector<SourceChangeRange>& diffs, 1321 int position) { 1322 auto it = std::lower_bound(diffs.begin(), diffs.end(), position, 1323 [](const SourceChangeRange& change, int position) { 1324 return change.end_position < position; 1325 }); 1326 if (it != diffs.end() && position == it->end_position) { 1327 return it->new_end_position; 1328 } 1329 if (it == diffs.begin()) return position; 1330 DCHECK(it == diffs.end() || position <= it->start_position); 1331 it = std::prev(it); 1332 return position + (it->new_end_position - it->end_position); 1333 } 1334 } // namespace internal 1335 } // namespace v8 1336