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