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