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