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