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