• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
Differencer(Comparator::Input * input)65   explicit Differencer(Comparator::Input* input)
66       : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
67     buffer_ = NewArray<int>(len1_ * len2_);
68   }
~Differencer()69   ~Differencer() {
70     DeleteArray(buffer_);
71   }
72 
Initialize()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.
FillTable()82   void FillTable() {
83     CompareUpToTail(0, 0);
84   }
85 
SaveResult(Comparator::Output * chunk_writer)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.
CompareUpToTail(int pos1,int pos2)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 
get_cell(int i1,int i2)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.
set_value4_and_dir(int i1,int i2,int value4,Direction dir)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 
get_value4(int i1,int i2)192   int get_value4(int i1, int i2) {
193     return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
194   }
get_direction(int i1,int i2)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).
StaticAssertHolder()205   void StaticAssertHolder() {
206     STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
207   }
208 
209   class ResultWriter {
210    public:
ResultWriter(Comparator::Output * chunk_writer)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     }
eq()215     void eq() {
216       FlushChunk();
217       pos1_++;
218       pos2_++;
219     }
skip1(int len1)220     void skip1(int len1) {
221       StartChunk();
222       pos1_ += len1;
223     }
skip2(int len2)224     void skip2(int len2) {
225       StartChunk();
226       pos2_ += len2;
227     }
close()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 
StartChunk()240     void StartChunk() {
241       if (!has_open_chunk_) {
242         pos1_begin_ = pos1_;
243         pos2_begin_ = pos2_;
244         has_open_chunk_ = true;
245       }
246     }
247 
FlushChunk()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 
CalculateDifference(Comparator::Input * input,Comparator::Output * result_writer)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 
CompareSubstrings(Handle<String> s1,int pos1,Handle<String> s2,int pos2,int len)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.
NarrowDownInput(SubrangableInput * input,SubrangableOutput * 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:
TokensCompareInput(Handle<String> s1,int offset1,int len1,Handle<String> s2,int offset2,int len2)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   }
GetLength1()341   int GetLength1() override { return len1_; }
GetLength2()342   int GetLength2() override { return len2_; }
Equals(int index1,int index2)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:
TokensCompareOutput(int offset1,int offset2,std::vector<SourceChangeRange> * output)360   TokensCompareOutput(int offset1, int offset2,
361                       std::vector<SourceChangeRange>* output)
362       : output_(output), offset1_(offset1), offset2_(offset2) {}
363 
AddChunk(int pos1,int pos2,int len1,int len2)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:
LineEndsWrapper(Isolate * isolate,Handle<String> string)380   explicit LineEndsWrapper(Isolate* isolate, Handle<String> string)
381       : ends_array_(String::CalculateLineEnds(isolate, string, false)),
382         string_len_(string->length()) {}
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.
GetLineStart(int index)388   int GetLineStart(int index) { return index == 0 ? 0 : GetLineEnd(index - 1); }
GetLineEnd(int index)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 
GetPosAfterNewLine(int index)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:
LineArrayCompareInput(Handle<String> s1,Handle<String> s2,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2)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   }
GetLength1()420   int GetLength1() override { return subrange_len1_; }
GetLength2()421   int GetLength2() override { return subrange_len2_; }
Equals(int index1,int index2)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   }
SetSubrange1(int offset,int len)438   void SetSubrange1(int offset, int len) override {
439     subrange_offset1_ = offset;
440     subrange_len1_ = len;
441   }
SetSubrange2(int offset,int len)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:
TokenizingLineArrayCompareOutput(Isolate * isolate,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2,Handle<String> s1,Handle<String> s2,std::vector<SourceChangeRange> * output)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 
AddChunk(int line_pos1,int line_pos2,int line_len1,int line_len2)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   }
SetSubrange1(int offset,int len)499   void SetSubrange1(int offset, int len) override {
500     subrange_offset1_ = offset;
501   }
SetSubrange2(int offset,int len)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 
SourcePositionEventv8::internal::__anonc79e164e0111::SourcePositionEvent530   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) {}
SourcePositionEventv8::internal::__anonc79e164e0111::SourcePositionEvent535   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 
LessThanv8::internal::__anonc79e164e0111::SourcePositionEvent541   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 
FunctionLiteralChangev8::internal::__anonc79e164e0111::FunctionLiteralChange579   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>;
CalculateFunctionLiteralChanges(const std::vector<FunctionLiteral * > & literals,const std::vector<SourceChangeRange> & diffs,FunctionLiteralChanges * result)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.
HasChangedScope(FunctionLiteral * a,FunctionLiteral * b)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*>;
MapLiterals(const FunctionLiteralChanges & changes,const std::vector<FunctionLiteral * > & new_literals,LiteralMap * unchanged,LiteralMap * changed)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:
CollectFunctionLiterals(Isolate * isolate,AstNode * root)732   CollectFunctionLiterals(Isolate* isolate, AstNode* root)
733       : AstTraversalVisitor<CollectFunctionLiterals>(isolate, root) {}
VisitFunctionLiteral(FunctionLiteral * lit)734   void VisitFunctionLiteral(FunctionLiteral* lit) {
735     AstTraversalVisitor::VisitFunctionLiteral(lit);
736     literals_->push_back(lit);
737   }
Run(std::vector<FunctionLiteral * > * literals)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 
ParseScript(Isolate * isolate,ParseInfo * parse_info,bool compile_as_well,std::vector<FunctionLiteral * > * literals,debug::LiveEditResult * result)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 {
FunctionDatav8::internal::__anonc79e164e0111::FunctionData781   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:
AddInterestingLiteral(int script_id,FunctionLiteral * literal,bool should_restart)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 
Lookup(SharedFunctionInfo * sfi,FunctionData ** data)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 
Lookup(Handle<Script> script,FunctionLiteral * literal,FunctionData ** data)821   bool Lookup(Handle<Script> script, FunctionLiteral* literal,
822               FunctionData** data) {
823     return Lookup(GetFuncId(script->id(), literal), data);
824   }
825 
Fill(Isolate * isolate,Address * restart_frame_fp)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 
GetFuncId(int script_id,FunctionLiteral * literal)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 
GetFuncId(int script_id,SharedFunctionInfo * sfi)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 
Lookup(FuncId id,FunctionData ** data)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 
VisitThread(Isolate * isolate,ThreadLocalTop * top)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 
CanPatchScript(const LiteralMap & changed,Handle<Script> script,Handle<Script> new_script,FunctionDataMap & function_data_map,debug::LiveEditResult * result)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 
CanRestartFrame(Isolate * isolate,Address fp,FunctionDataMap & function_data_map,const LiteralMap & changed,debug::LiveEditResult * result)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 
TranslateSourcePositionTable(Isolate * isolate,Handle<BytecodeArray> code,const std::vector<SourceChangeRange> & diffs)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 
UpdatePositions(Isolate * isolate,Handle<SharedFunctionInfo> sfi,const std::vector<SourceChangeRange> & diffs)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 
PatchScript(Isolate * isolate,Handle<Script> script,Handle<String> new_source,bool preview,debug::LiveEditResult * result)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 
InitializeThreadLocal(Debug * debug)1266 void LiveEdit::InitializeThreadLocal(Debug* debug) {
1267   debug->thread_local_.restart_fp_ = 0;
1268 }
1269 
RestartFrame(JavaScriptFrame * frame)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 
CompareStrings(Isolate * isolate,Handle<String> s1,Handle<String> s2,std::vector<SourceChangeRange> * diffs)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 
TranslatePosition(const std::vector<SourceChangeRange> & diffs,int position)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