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