Home | History | Annotate | Download | only in debug
      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