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 
28 namespace v8 {
29 namespace internal {
30 namespace {
31 // A general-purpose comparator between 2 arrays.
32 class 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.
65 class Differencer {
66  public:
Differencer(Comparator::Input * input)67   explicit Differencer(Comparator::Input* input)
68       : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
69   }
70 
Initialize()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.
FillTable()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 
SaveResult(Comparator::Output * chunk_writer)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.
CompareUpToTail(int pos1,int pos2)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 
get_cell(int i1,int i2)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 
set_cell(int i1,int i2,int value)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.
set_value4_and_dir(int i1,int i2,int value4,Direction dir)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 
get_value4(int i1,int i2)199   int get_value4(int i1, int i2) {
200     return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
201   }
get_direction(int i1,int i2)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).
StaticAssertHolder()212   void StaticAssertHolder() {
213     STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
214   }
215 
216   class ResultWriter {
217    public:
ResultWriter(Comparator::Output * chunk_writer)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     }
eq(int len=1)222     void eq(int len = 1) {
223       FlushChunk();
224       pos1_ += len;
225       pos2_ += len;
226     }
skip1(int len1)227     void skip1(int len1) {
228       StartChunk();
229       pos1_ += len1;
230     }
skip2(int len2)231     void skip2(int len2) {
232       StartChunk();
233       pos2_ += len2;
234     }
close()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 
StartChunk()247     void StartChunk() {
248       if (!has_open_chunk_) {
249         pos1_begin_ = pos1_;
250         pos2_begin_ = pos2_;
251         has_open_chunk_ = true;
252       }
253     }
254 
FlushChunk()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 
CalculateDifference(Comparator::Input * input,Comparator::Output * result_writer)265 void 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 
CompareSubstrings(Handle<String> s1,int pos1,Handle<String> s2,int pos2,int len)273 bool 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.
285 class 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 
292 class 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.
NarrowDownInput(SubrangableInput * input,SubrangableOutput * output)300 void 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.
341 class TokensCompareInput : public Comparator::Input {
342  public:
TokensCompareInput(Handle<String> s1,int offset1,int len1,Handle<String> s2,int offset2,int len2)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   }
GetLength1()348   int GetLength1() override { return len1_; }
GetLength2()349   int GetLength2() override { return len2_; }
Equals(int index1,int index2)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.
365 class TokensCompareOutput : public Comparator::Output {
366  public:
TokensCompareOutput(int offset1,int offset2,std::vector<SourceChangeRange> * output)367   TokensCompareOutput(int offset1, int offset2,
368                       std::vector<SourceChangeRange>* output)
369       : output_(output), offset1_(offset1), offset2_(offset2) {}
370 
AddChunk(int pos1,int pos2,int len1,int len2)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.
385 class LineEndsWrapper {
386  public:
LineEndsWrapper(Isolate * isolate,Handle<String> string)387   explicit LineEndsWrapper(Isolate* isolate, Handle<String> string)
388       : ends_array_(String::CalculateLineEnds(isolate, string, false)),
389         string_len_(string->length()) {}
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.
GetLineStart(int index)395   int GetLineStart(int index) { return index == 0 ? 0 : GetLineEnd(index - 1); }
GetLineEnd(int index)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 
GetPosAfterNewLine(int index)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.
417 class LineArrayCompareInput : public SubrangableInput {
418  public:
LineArrayCompareInput(Handle<String> s1,Handle<String> s2,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2)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   }
GetLength1()427   int GetLength1() override { return subrange_len1_; }
GetLength2()428   int GetLength2() override { return subrange_len2_; }
Equals(int index1,int index2)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   }
SetSubrange1(int offset,int len)445   void SetSubrange1(int offset, int len) override {
446     subrange_offset1_ = offset;
447     subrange_len1_ = len;
448   }
SetSubrange2(int offset,int len)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.
467 class TokenizingLineArrayCompareOutput : public SubrangableOutput {
468  public:
TokenizingLineArrayCompareOutput(Isolate * isolate,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2,Handle<String> s1,Handle<String> s2,std::vector<SourceChangeRange> * output)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 
AddChunk(int line_pos1,int line_pos2,int line_len1,int line_len2)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   }
SetSubrange1(int offset,int len)506   void SetSubrange1(int offset, int len) override {
507     subrange_offset1_ = offset;
508   }
SetSubrange2(int offset,int len)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 
526 struct 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 
SourcePositionEventv8::internal::__anondac13d6e0111::SourcePositionEvent537   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) {}
SourcePositionEventv8::internal::__anondac13d6e0111::SourcePositionEvent542   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 
LessThanv8::internal::__anondac13d6e0111::SourcePositionEvent548   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 
578 struct 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 
FunctionLiteralChangev8::internal::__anondac13d6e0111::FunctionLiteralChange586   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 
593 using FunctionLiteralChanges =
594     std::unordered_map<FunctionLiteral*, FunctionLiteralChange>;
CalculateFunctionLiteralChanges(const std::vector<FunctionLiteral * > & literals,const std::vector<SourceChangeRange> & diffs,FunctionLiteralChanges * result)595 void 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.
HasChangedScope(FunctionLiteral * a,FunctionLiteral * b)658 bool 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 
679 enum ChangeState { UNCHANGED, CHANGED, DAMAGED };
680 
681 using LiteralMap = std::unordered_map<FunctionLiteral*, FunctionLiteral*>;
MapLiterals(const FunctionLiteralChanges & changes,const std::vector<FunctionLiteral * > & new_literals,LiteralMap * unchanged,LiteralMap * changed)682 void 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 
736 class CollectFunctionLiterals final
737     : public AstTraversalVisitor<CollectFunctionLiterals> {
738  public:
CollectFunctionLiterals(Isolate * isolate,AstNode * root)739   CollectFunctionLiterals(Isolate* isolate, AstNode* root)
740       : AstTraversalVisitor<CollectFunctionLiterals>(isolate, root) {}
VisitFunctionLiteral(FunctionLiteral * lit)741   void VisitFunctionLiteral(FunctionLiteral* lit) {
742     AstTraversalVisitor::VisitFunctionLiteral(lit);
743     literals_->push_back(lit);
744   }
Run(std::vector<FunctionLiteral * > * literals)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 
ParseScript(Isolate * isolate,Handle<Script> script,ParseInfo * parse_info,bool compile_as_well,std::vector<FunctionLiteral * > * literals,debug::LiveEditResult * result)755 bool 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 
789 struct FunctionData {
FunctionDatav8::internal::__anondac13d6e0111::FunctionData790   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 
804 class FunctionDataMap : public ThreadVisitor {
805  public:
AddInterestingLiteral(int script_id,FunctionLiteral * literal)806   void AddInterestingLiteral(int script_id, FunctionLiteral* literal) {
807     map_.emplace(GetFuncId(script_id, literal), FunctionData{literal});
808   }
809 
Lookup(SharedFunctionInfo sfi,FunctionData ** data)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 
Lookup(Handle<Script> script,FunctionLiteral * literal,FunctionData ** data)819   bool Lookup(Handle<Script> script, FunctionLiteral* literal,
820               FunctionData** data) {
821     return Lookup(GetFuncId(script->id(), literal), data);
822   }
823 
Fill(Isolate * isolate)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 
GetFuncId(int script_id,FunctionLiteral * literal)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 
GetFuncId(int script_id,SharedFunctionInfo sfi)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 
Lookup(FuncId id,FunctionData ** data)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 
VisitThread(Isolate * isolate,ThreadLocalTop * top)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 
CanPatchScript(const LiteralMap & changed,Handle<Script> script,Handle<Script> new_script,FunctionDataMap & function_data_map,debug::LiveEditResult * result)910 bool 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 
TranslateSourcePositionTable(Isolate * isolate,Handle<BytecodeArray> code,const std::vector<SourceChangeRange> & diffs)933 void 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 
UpdatePositions(Isolate * isolate,Handle<SharedFunctionInfo> sfi,const std::vector<SourceChangeRange> & diffs)957 void 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 
PatchScript(Isolate * isolate,Handle<Script> script,Handle<String> new_source,bool preview,debug::LiveEditResult * result)975 void 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   UnoptimizedCompileState compile_state(isolate);
988   UnoptimizedCompileFlags flags =
989       UnoptimizedCompileFlags::ForScriptCompile(isolate, *script);
990   flags.set_is_eager(true);
991   ParseInfo parse_info(isolate, flags, &compile_state);
992   std::vector<FunctionLiteral*> literals;
993   if (!ParseScript(isolate, script, &parse_info, false, &literals, result))
994     return;
995 
996   Handle<Script> new_script = isolate->factory()->CloneScript(script);
997   new_script->set_source(*new_source);
998   UnoptimizedCompileState new_compile_state(isolate);
999   UnoptimizedCompileFlags new_flags =
1000       UnoptimizedCompileFlags::ForScriptCompile(isolate, *new_script);
1001   new_flags.set_is_eager(true);
1002   ParseInfo new_parse_info(isolate, new_flags, &new_compile_state);
1003   std::vector<FunctionLiteral*> new_literals;
1004   if (!ParseScript(isolate, new_script, &new_parse_info, true, &new_literals,
1005                    result)) {
1006     return;
1007   }
1008 
1009   FunctionLiteralChanges literal_changes;
1010   CalculateFunctionLiteralChanges(literals, diffs, &literal_changes);
1011 
1012   LiteralMap changed;
1013   LiteralMap unchanged;
1014   MapLiterals(literal_changes, new_literals, &unchanged, &changed);
1015 
1016   FunctionDataMap function_data_map;
1017   for (const auto& mapping : changed) {
1018     function_data_map.AddInterestingLiteral(script->id(), mapping.first);
1019     function_data_map.AddInterestingLiteral(new_script->id(), mapping.second);
1020   }
1021   for (const auto& mapping : unchanged) {
1022     function_data_map.AddInterestingLiteral(script->id(), mapping.first);
1023   }
1024   function_data_map.Fill(isolate);
1025 
1026   if (!CanPatchScript(changed, script, new_script, function_data_map, result)) {
1027     return;
1028   }
1029 
1030   if (preview) {
1031     result->status = debug::LiveEditResult::OK;
1032     return;
1033   }
1034 
1035   // Patching a script means that the bytecode on the stack may no longer
1036   // correspond to the bytecode of the JSFunction for that frame. As a result
1037   // it is no longer safe to flush bytecode since we might flush the new
1038   // bytecode for a JSFunction that is on the stack with an old bytecode, which
1039   // breaks the invariant that any JSFunction active on the stack is compiled.
1040   isolate->set_disable_bytecode_flushing(true);
1041 
1042   std::map<int, int> start_position_to_unchanged_id;
1043   for (const auto& mapping : unchanged) {
1044     FunctionData* data = nullptr;
1045     if (!function_data_map.Lookup(script, mapping.first, &data)) continue;
1046     Handle<SharedFunctionInfo> sfi;
1047     if (!data->shared.ToHandle(&sfi)) continue;
1048     DCHECK_EQ(sfi->script(), *script);
1049 
1050     isolate->compilation_cache()->Remove(sfi);
1051     isolate->debug()->DeoptimizeFunction(sfi);
1052     if (sfi->HasDebugInfo()) {
1053       Handle<DebugInfo> debug_info(sfi->GetDebugInfo(), isolate);
1054       isolate->debug()->RemoveBreakInfoAndMaybeFree(debug_info);
1055     }
1056     SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, sfi);
1057     UpdatePositions(isolate, sfi, diffs);
1058 
1059     sfi->set_script(*new_script);
1060     sfi->set_function_literal_id(mapping.second->function_literal_id());
1061     new_script->shared_function_infos().Set(
1062         mapping.second->function_literal_id(), HeapObjectReference::Weak(*sfi));
1063     DCHECK_EQ(sfi->function_literal_id(),
1064               mapping.second->function_literal_id());
1065 
1066     // Save the new start_position -> id mapping, so that we can recover it when
1067     // iterating over changed functions' constant pools.
1068     start_position_to_unchanged_id[mapping.second->start_position()] =
1069         mapping.second->function_literal_id();
1070 
1071     if (sfi->HasUncompiledDataWithPreparseData()) {
1072       sfi->ClearPreparseData();
1073     }
1074 
1075     for (auto& js_function : data->js_functions) {
1076       js_function->set_raw_feedback_cell(
1077           *isolate->factory()->many_closures_cell());
1078       if (!js_function->is_compiled()) continue;
1079       IsCompiledScope is_compiled_scope(
1080           js_function->shared().is_compiled_scope(isolate));
1081       JSFunction::EnsureFeedbackVector(js_function, &is_compiled_scope);
1082     }
1083 
1084     if (!sfi->HasBytecodeArray()) continue;
1085     FixedArray constants = sfi->GetBytecodeArray(isolate).constant_pool();
1086     for (int i = 0; i < constants.length(); ++i) {
1087       if (!constants.get(i).IsSharedFunctionInfo()) continue;
1088       FunctionData* data = nullptr;
1089       if (!function_data_map.Lookup(SharedFunctionInfo::cast(constants.get(i)),
1090                                     &data)) {
1091         continue;
1092       }
1093       auto change_it = changed.find(data->literal);
1094       if (change_it == changed.end()) continue;
1095       if (!function_data_map.Lookup(new_script, change_it->second, &data)) {
1096         continue;
1097       }
1098       Handle<SharedFunctionInfo> new_sfi;
1099       if (!data->shared.ToHandle(&new_sfi)) continue;
1100       constants.set(i, *new_sfi);
1101     }
1102   }
1103   for (const auto& mapping : changed) {
1104     FunctionData* data = nullptr;
1105     if (!function_data_map.Lookup(new_script, mapping.second, &data)) continue;
1106     Handle<SharedFunctionInfo> new_sfi = data->shared.ToHandleChecked();
1107     DCHECK_EQ(new_sfi->script(), *new_script);
1108 
1109     if (!function_data_map.Lookup(script, mapping.first, &data)) continue;
1110     Handle<SharedFunctionInfo> sfi;
1111     if (!data->shared.ToHandle(&sfi)) continue;
1112 
1113     isolate->debug()->DeoptimizeFunction(sfi);
1114     isolate->compilation_cache()->Remove(sfi);
1115     for (auto& js_function : data->js_functions) {
1116       js_function->set_shared(*new_sfi);
1117       js_function->set_code(js_function->shared().GetCode(), kReleaseStore);
1118 
1119       js_function->set_raw_feedback_cell(
1120           *isolate->factory()->many_closures_cell());
1121       if (!js_function->is_compiled()) continue;
1122       IsCompiledScope is_compiled_scope(
1123           js_function->shared().is_compiled_scope(isolate));
1124       JSFunction::EnsureFeedbackVector(js_function, &is_compiled_scope);
1125     }
1126   }
1127   SharedFunctionInfo::ScriptIterator it(isolate, *new_script);
1128   for (SharedFunctionInfo sfi = it.Next(); !sfi.is_null(); sfi = it.Next()) {
1129     if (!sfi.HasBytecodeArray()) continue;
1130     FixedArray constants = sfi.GetBytecodeArray(isolate).constant_pool();
1131     for (int i = 0; i < constants.length(); ++i) {
1132       if (!constants.get(i).IsSharedFunctionInfo()) continue;
1133       SharedFunctionInfo inner_sfi = SharedFunctionInfo::cast(constants.get(i));
1134       // See if there is a mapping from this function's start position to a
1135       // unchanged function's id.
1136       auto unchanged_it =
1137           start_position_to_unchanged_id.find(inner_sfi.StartPosition());
1138       if (unchanged_it == start_position_to_unchanged_id.end()) continue;
1139 
1140       // Grab that function id from the new script's SFI list, which should have
1141       // already been updated in in the unchanged pass.
1142       SharedFunctionInfo old_unchanged_inner_sfi =
1143           SharedFunctionInfo::cast(new_script->shared_function_infos()
1144                                        .Get(unchanged_it->second)
1145                                        ->GetHeapObject());
1146       if (old_unchanged_inner_sfi == inner_sfi) continue;
1147       DCHECK_NE(old_unchanged_inner_sfi, inner_sfi);
1148       // Now some sanity checks. Make sure that the unchanged SFI has already
1149       // been processed and patched to be on the new script ...
1150       DCHECK_EQ(old_unchanged_inner_sfi.script(), *new_script);
1151       constants.set(i, old_unchanged_inner_sfi);
1152     }
1153   }
1154 #ifdef DEBUG
1155   {
1156     // Check that all the functions in the new script are valid, that their
1157     // function literals match what is expected, and that start positions are
1158     // unique.
1159     DisallowGarbageCollection no_gc;
1160 
1161     SharedFunctionInfo::ScriptIterator it(isolate, *new_script);
1162     std::set<int> start_positions;
1163     for (SharedFunctionInfo sfi = it.Next(); !sfi.is_null(); sfi = it.Next()) {
1164       DCHECK_EQ(sfi.script(), *new_script);
1165       DCHECK_EQ(sfi.function_literal_id(), it.CurrentIndex());
1166       // Don't check the start position of the top-level function, as it can
1167       // overlap with a function in the script.
1168       if (sfi.is_toplevel()) {
1169         DCHECK_EQ(start_positions.find(sfi.StartPosition()),
1170                   start_positions.end());
1171         start_positions.insert(sfi.StartPosition());
1172       }
1173 
1174       if (!sfi.HasBytecodeArray()) continue;
1175       // Check that all the functions in this function's constant pool are also
1176       // on the new script, and that their id matches their index in the new
1177       // scripts function list.
1178       FixedArray constants = sfi.GetBytecodeArray(isolate).constant_pool();
1179       for (int i = 0; i < constants.length(); ++i) {
1180         if (!constants.get(i).IsSharedFunctionInfo()) continue;
1181         SharedFunctionInfo inner_sfi =
1182             SharedFunctionInfo::cast(constants.get(i));
1183         DCHECK_EQ(inner_sfi.script(), *new_script);
1184         DCHECK_EQ(inner_sfi, new_script->shared_function_infos()
1185                                  .Get(inner_sfi.function_literal_id())
1186                                  ->GetHeapObject());
1187       }
1188     }
1189   }
1190 #endif
1191 
1192   int script_id = script->id();
1193   script->set_id(new_script->id());
1194   new_script->set_id(script_id);
1195   result->status = debug::LiveEditResult::OK;
1196   result->script = ToApiHandle<v8::debug::Script>(new_script);
1197 }
1198 
CompareStrings(Isolate * isolate,Handle<String> s1,Handle<String> s2,std::vector<SourceChangeRange> * diffs)1199 void LiveEdit::CompareStrings(Isolate* isolate, Handle<String> s1,
1200                               Handle<String> s2,
1201                               std::vector<SourceChangeRange>* diffs) {
1202   s1 = String::Flatten(isolate, s1);
1203   s2 = String::Flatten(isolate, s2);
1204 
1205   LineEndsWrapper line_ends1(isolate, s1);
1206   LineEndsWrapper line_ends2(isolate, s2);
1207 
1208   LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
1209   TokenizingLineArrayCompareOutput output(isolate, line_ends1, line_ends2, s1,
1210                                           s2, diffs);
1211 
1212   NarrowDownInput(&input, &output);
1213 
1214   Comparator::CalculateDifference(&input, &output);
1215 }
1216 
TranslatePosition(const std::vector<SourceChangeRange> & diffs,int position)1217 int LiveEdit::TranslatePosition(const std::vector<SourceChangeRange>& diffs,
1218                                 int position) {
1219   auto it = std::lower_bound(diffs.begin(), diffs.end(), position,
1220                              [](const SourceChangeRange& change, int position) {
1221                                return change.end_position < position;
1222                              });
1223   if (it != diffs.end() && position == it->end_position) {
1224     return it->new_end_position;
1225   }
1226   if (it == diffs.begin()) return position;
1227   DCHECK(it == diffs.end() || position <= it->start_position);
1228   it = std::prev(it);
1229   return position + (it->new_end_position - it->end_position);
1230 }
1231 }  // namespace internal
1232 }  // namespace v8
1233