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/ast/scopes.h"
8 #include "src/code-stubs.h"
9 #include "src/compilation-cache.h"
10 #include "src/compiler.h"
11 #include "src/debug/debug.h"
12 #include "src/deoptimizer.h"
13 #include "src/frames-inl.h"
14 #include "src/global-handles.h"
15 #include "src/isolate-inl.h"
16 #include "src/messages.h"
17 #include "src/source-position-table.h"
18 #include "src/v8.h"
19 #include "src/v8memory.h"
20 
21 namespace v8 {
22 namespace internal {
23 
SetElementSloppy(Handle<JSObject> object,uint32_t index,Handle<Object> value)24 void SetElementSloppy(Handle<JSObject> object,
25                       uint32_t index,
26                       Handle<Object> value) {
27   // Ignore return value from SetElement. It can only be a failure if there
28   // are element setters causing exceptions and the debugger context has none
29   // of these.
30   Object::SetElement(object->GetIsolate(), object, index, value, SLOPPY)
31       .Assert();
32 }
33 
34 
35 // A simple implementation of dynamic programming algorithm. It solves
36 // the problem of finding the difference of 2 arrays. It uses a table of results
37 // of subproblems. Each cell contains a number together with 2-bit flag
38 // that helps building the chunk list.
39 class Differencer {
40  public:
Differencer(Comparator::Input * input)41   explicit Differencer(Comparator::Input* input)
42       : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
43     buffer_ = NewArray<int>(len1_ * len2_);
44   }
~Differencer()45   ~Differencer() {
46     DeleteArray(buffer_);
47   }
48 
Initialize()49   void Initialize() {
50     int array_size = len1_ * len2_;
51     for (int i = 0; i < array_size; i++) {
52       buffer_[i] = kEmptyCellValue;
53     }
54   }
55 
56   // Makes sure that result for the full problem is calculated and stored
57   // in the table together with flags showing a path through subproblems.
FillTable()58   void FillTable() {
59     CompareUpToTail(0, 0);
60   }
61 
SaveResult(Comparator::Output * chunk_writer)62   void SaveResult(Comparator::Output* chunk_writer) {
63     ResultWriter writer(chunk_writer);
64 
65     int pos1 = 0;
66     int pos2 = 0;
67     while (true) {
68       if (pos1 < len1_) {
69         if (pos2 < len2_) {
70           Direction dir = get_direction(pos1, pos2);
71           switch (dir) {
72             case EQ:
73               writer.eq();
74               pos1++;
75               pos2++;
76               break;
77             case SKIP1:
78               writer.skip1(1);
79               pos1++;
80               break;
81             case SKIP2:
82             case SKIP_ANY:
83               writer.skip2(1);
84               pos2++;
85               break;
86             default:
87               UNREACHABLE();
88           }
89         } else {
90           writer.skip1(len1_ - pos1);
91           break;
92         }
93       } else {
94         if (len2_ != pos2) {
95           writer.skip2(len2_ - pos2);
96         }
97         break;
98       }
99     }
100     writer.close();
101   }
102 
103  private:
104   Comparator::Input* input_;
105   int* buffer_;
106   int len1_;
107   int len2_;
108 
109   enum Direction {
110     EQ = 0,
111     SKIP1,
112     SKIP2,
113     SKIP_ANY,
114 
115     MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
116   };
117 
118   // Computes result for a subtask and optionally caches it in the buffer table.
119   // All results values are shifted to make space for flags in the lower bits.
CompareUpToTail(int pos1,int pos2)120   int CompareUpToTail(int pos1, int pos2) {
121     if (pos1 < len1_) {
122       if (pos2 < len2_) {
123         int cached_res = get_value4(pos1, pos2);
124         if (cached_res == kEmptyCellValue) {
125           Direction dir;
126           int res;
127           if (input_->Equals(pos1, pos2)) {
128             res = CompareUpToTail(pos1 + 1, pos2 + 1);
129             dir = EQ;
130           } else {
131             int res1 = CompareUpToTail(pos1 + 1, pos2) +
132                 (1 << kDirectionSizeBits);
133             int res2 = CompareUpToTail(pos1, pos2 + 1) +
134                 (1 << kDirectionSizeBits);
135             if (res1 == res2) {
136               res = res1;
137               dir = SKIP_ANY;
138             } else if (res1 < res2) {
139               res = res1;
140               dir = SKIP1;
141             } else {
142               res = res2;
143               dir = SKIP2;
144             }
145           }
146           set_value4_and_dir(pos1, pos2, res, dir);
147           cached_res = res;
148         }
149         return cached_res;
150       } else {
151         return (len1_ - pos1) << kDirectionSizeBits;
152       }
153     } else {
154       return (len2_ - pos2) << kDirectionSizeBits;
155     }
156   }
157 
get_cell(int i1,int i2)158   inline int& get_cell(int i1, int i2) {
159     return buffer_[i1 + i2 * len1_];
160   }
161 
162   // Each cell keeps a value plus direction. Value is multiplied by 4.
set_value4_and_dir(int i1,int i2,int value4,Direction dir)163   void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
164     DCHECK((value4 & kDirectionMask) == 0);
165     get_cell(i1, i2) = value4 | dir;
166   }
167 
get_value4(int i1,int i2)168   int get_value4(int i1, int i2) {
169     return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
170   }
get_direction(int i1,int i2)171   Direction get_direction(int i1, int i2) {
172     return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
173   }
174 
175   static const int kDirectionSizeBits = 2;
176   static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
177   static const int kEmptyCellValue = ~0u << kDirectionSizeBits;
178 
179   // This method only holds static assert statement (unfortunately you cannot
180   // place one in class scope).
StaticAssertHolder()181   void StaticAssertHolder() {
182     STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
183   }
184 
185   class ResultWriter {
186    public:
ResultWriter(Comparator::Output * chunk_writer)187     explicit ResultWriter(Comparator::Output* chunk_writer)
188         : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
189           pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
190     }
eq()191     void eq() {
192       FlushChunk();
193       pos1_++;
194       pos2_++;
195     }
skip1(int len1)196     void skip1(int len1) {
197       StartChunk();
198       pos1_ += len1;
199     }
skip2(int len2)200     void skip2(int len2) {
201       StartChunk();
202       pos2_ += len2;
203     }
close()204     void close() {
205       FlushChunk();
206     }
207 
208    private:
209     Comparator::Output* chunk_writer_;
210     int pos1_;
211     int pos2_;
212     int pos1_begin_;
213     int pos2_begin_;
214     bool has_open_chunk_;
215 
StartChunk()216     void StartChunk() {
217       if (!has_open_chunk_) {
218         pos1_begin_ = pos1_;
219         pos2_begin_ = pos2_;
220         has_open_chunk_ = true;
221       }
222     }
223 
FlushChunk()224     void FlushChunk() {
225       if (has_open_chunk_) {
226         chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
227                                 pos1_ - pos1_begin_, pos2_ - pos2_begin_);
228         has_open_chunk_ = false;
229       }
230     }
231   };
232 };
233 
234 
CalculateDifference(Comparator::Input * input,Comparator::Output * result_writer)235 void Comparator::CalculateDifference(Comparator::Input* input,
236                                      Comparator::Output* result_writer) {
237   Differencer differencer(input);
238   differencer.Initialize();
239   differencer.FillTable();
240   differencer.SaveResult(result_writer);
241 }
242 
243 
CompareSubstrings(Handle<String> s1,int pos1,Handle<String> s2,int pos2,int len)244 static bool CompareSubstrings(Handle<String> s1, int pos1,
245                               Handle<String> s2, int pos2, int len) {
246   for (int i = 0; i < len; i++) {
247     if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
248       return false;
249     }
250   }
251   return true;
252 }
253 
254 
255 // Additional to Input interface. Lets switch Input range to subrange.
256 // More elegant way would be to wrap one Input as another Input object
257 // and translate positions there, but that would cost us additional virtual
258 // call per comparison.
259 class SubrangableInput : public Comparator::Input {
260  public:
261   virtual void SetSubrange1(int offset, int len) = 0;
262   virtual void SetSubrange2(int offset, int len) = 0;
263 };
264 
265 
266 class SubrangableOutput : public Comparator::Output {
267  public:
268   virtual void SetSubrange1(int offset, int len) = 0;
269   virtual void SetSubrange2(int offset, int len) = 0;
270 };
271 
272 
min(int a,int b)273 static int min(int a, int b) {
274   return a < b ? a : b;
275 }
276 
277 
278 // Finds common prefix and suffix in input. This parts shouldn't take space in
279 // linear programming table. Enable subranging in input and output.
NarrowDownInput(SubrangableInput * input,SubrangableOutput * output)280 static void NarrowDownInput(SubrangableInput* input,
281     SubrangableOutput* output) {
282   const int len1 = input->GetLength1();
283   const int len2 = input->GetLength2();
284 
285   int common_prefix_len;
286   int common_suffix_len;
287 
288   {
289     common_prefix_len = 0;
290     int prefix_limit = min(len1, len2);
291     while (common_prefix_len < prefix_limit &&
292         input->Equals(common_prefix_len, common_prefix_len)) {
293       common_prefix_len++;
294     }
295 
296     common_suffix_len = 0;
297     int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
298 
299     while (common_suffix_len < suffix_limit &&
300         input->Equals(len1 - common_suffix_len - 1,
301         len2 - common_suffix_len - 1)) {
302       common_suffix_len++;
303     }
304   }
305 
306   if (common_prefix_len > 0 || common_suffix_len > 0) {
307     int new_len1 = len1 - common_suffix_len - common_prefix_len;
308     int new_len2 = len2 - common_suffix_len - common_prefix_len;
309 
310     input->SetSubrange1(common_prefix_len, new_len1);
311     input->SetSubrange2(common_prefix_len, new_len2);
312 
313     output->SetSubrange1(common_prefix_len, new_len1);
314     output->SetSubrange2(common_prefix_len, new_len2);
315   }
316 }
317 
318 
319 // A helper class that writes chunk numbers into JSArray.
320 // Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
321 class CompareOutputArrayWriter {
322  public:
CompareOutputArrayWriter(Isolate * isolate)323   explicit CompareOutputArrayWriter(Isolate* isolate)
324       : array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
325 
GetResult()326   Handle<JSArray> GetResult() {
327     return array_;
328   }
329 
WriteChunk(int char_pos1,int char_pos2,int char_len1,int char_len2)330   void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
331     Isolate* isolate = array_->GetIsolate();
332     SetElementSloppy(array_,
333                      current_size_,
334                      Handle<Object>(Smi::FromInt(char_pos1), isolate));
335     SetElementSloppy(array_,
336                      current_size_ + 1,
337                      Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
338                                     isolate));
339     SetElementSloppy(array_,
340                      current_size_ + 2,
341                      Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
342                                     isolate));
343     current_size_ += 3;
344   }
345 
346  private:
347   Handle<JSArray> array_;
348   int current_size_;
349 };
350 
351 
352 // Represents 2 strings as 2 arrays of tokens.
353 // TODO(LiveEdit): Currently it's actually an array of charactres.
354 //     Make array of tokens instead.
355 class TokensCompareInput : public Comparator::Input {
356  public:
TokensCompareInput(Handle<String> s1,int offset1,int len1,Handle<String> s2,int offset2,int len2)357   TokensCompareInput(Handle<String> s1, int offset1, int len1,
358                        Handle<String> s2, int offset2, int len2)
359       : s1_(s1), offset1_(offset1), len1_(len1),
360         s2_(s2), offset2_(offset2), len2_(len2) {
361   }
GetLength1()362   virtual int GetLength1() {
363     return len1_;
364   }
GetLength2()365   virtual int GetLength2() {
366     return len2_;
367   }
Equals(int index1,int index2)368   bool Equals(int index1, int index2) {
369     return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
370   }
371 
372  private:
373   Handle<String> s1_;
374   int offset1_;
375   int len1_;
376   Handle<String> s2_;
377   int offset2_;
378   int len2_;
379 };
380 
381 
382 // Stores compare result in JSArray. Converts substring positions
383 // to absolute positions.
384 class TokensCompareOutput : public Comparator::Output {
385  public:
TokensCompareOutput(CompareOutputArrayWriter * array_writer,int offset1,int offset2)386   TokensCompareOutput(CompareOutputArrayWriter* array_writer,
387                       int offset1, int offset2)
388         : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
389   }
390 
AddChunk(int pos1,int pos2,int len1,int len2)391   void AddChunk(int pos1, int pos2, int len1, int len2) {
392     array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
393   }
394 
395  private:
396   CompareOutputArrayWriter* array_writer_;
397   int offset1_;
398   int offset2_;
399 };
400 
401 
402 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
403 // never has terminating new line character.
404 class LineEndsWrapper {
405  public:
LineEndsWrapper(Handle<String> string)406   explicit LineEndsWrapper(Handle<String> string)
407       : ends_array_(String::CalculateLineEnds(string, false)),
408         string_len_(string->length()) {
409   }
length()410   int length() {
411     return ends_array_->length() + 1;
412   }
413   // Returns start for any line including start of the imaginary line after
414   // the last line.
GetLineStart(int index)415   int GetLineStart(int index) {
416     if (index == 0) {
417       return 0;
418     } else {
419       return GetLineEnd(index - 1);
420     }
421   }
GetLineEnd(int index)422   int GetLineEnd(int index) {
423     if (index == ends_array_->length()) {
424       // End of the last line is always an end of the whole string.
425       // If the string ends with a new line character, the last line is an
426       // empty string after this character.
427       return string_len_;
428     } else {
429       return GetPosAfterNewLine(index);
430     }
431   }
432 
433  private:
434   Handle<FixedArray> ends_array_;
435   int string_len_;
436 
GetPosAfterNewLine(int index)437   int GetPosAfterNewLine(int index) {
438     return Smi::cast(ends_array_->get(index))->value() + 1;
439   }
440 };
441 
442 
443 // Represents 2 strings as 2 arrays of lines.
444 class LineArrayCompareInput : public SubrangableInput {
445  public:
LineArrayCompareInput(Handle<String> s1,Handle<String> s2,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2)446   LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
447                         LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
448       : s1_(s1), s2_(s2), line_ends1_(line_ends1),
449         line_ends2_(line_ends2),
450         subrange_offset1_(0), subrange_offset2_(0),
451         subrange_len1_(line_ends1_.length()),
452         subrange_len2_(line_ends2_.length()) {
453   }
GetLength1()454   int GetLength1() {
455     return subrange_len1_;
456   }
GetLength2()457   int GetLength2() {
458     return subrange_len2_;
459   }
Equals(int index1,int index2)460   bool Equals(int index1, int index2) {
461     index1 += subrange_offset1_;
462     index2 += subrange_offset2_;
463 
464     int line_start1 = line_ends1_.GetLineStart(index1);
465     int line_start2 = line_ends2_.GetLineStart(index2);
466     int line_end1 = line_ends1_.GetLineEnd(index1);
467     int line_end2 = line_ends2_.GetLineEnd(index2);
468     int len1 = line_end1 - line_start1;
469     int len2 = line_end2 - line_start2;
470     if (len1 != len2) {
471       return false;
472     }
473     return CompareSubstrings(s1_, line_start1, s2_, line_start2,
474                              len1);
475   }
SetSubrange1(int offset,int len)476   void SetSubrange1(int offset, int len) {
477     subrange_offset1_ = offset;
478     subrange_len1_ = len;
479   }
SetSubrange2(int offset,int len)480   void SetSubrange2(int offset, int len) {
481     subrange_offset2_ = offset;
482     subrange_len2_ = len;
483   }
484 
485  private:
486   Handle<String> s1_;
487   Handle<String> s2_;
488   LineEndsWrapper line_ends1_;
489   LineEndsWrapper line_ends2_;
490   int subrange_offset1_;
491   int subrange_offset2_;
492   int subrange_len1_;
493   int subrange_len2_;
494 };
495 
496 
497 // Stores compare result in JSArray. For each chunk tries to conduct
498 // a fine-grained nested diff token-wise.
499 class TokenizingLineArrayCompareOutput : public SubrangableOutput {
500  public:
TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,LineEndsWrapper line_ends2,Handle<String> s1,Handle<String> s2)501   TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
502                                    LineEndsWrapper line_ends2,
503                                    Handle<String> s1, Handle<String> s2)
504       : array_writer_(s1->GetIsolate()),
505         line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
506         subrange_offset1_(0), subrange_offset2_(0) {
507   }
508 
AddChunk(int line_pos1,int line_pos2,int line_len1,int line_len2)509   void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
510     line_pos1 += subrange_offset1_;
511     line_pos2 += subrange_offset2_;
512 
513     int char_pos1 = line_ends1_.GetLineStart(line_pos1);
514     int char_pos2 = line_ends2_.GetLineStart(line_pos2);
515     int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
516     int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
517 
518     if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
519       // Chunk is small enough to conduct a nested token-level diff.
520       HandleScope subTaskScope(s1_->GetIsolate());
521 
522       TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
523                                       s2_, char_pos2, char_len2);
524       TokensCompareOutput tokens_output(&array_writer_, char_pos1,
525                                           char_pos2);
526 
527       Comparator::CalculateDifference(&tokens_input, &tokens_output);
528     } else {
529       array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
530     }
531   }
SetSubrange1(int offset,int len)532   void SetSubrange1(int offset, int len) {
533     subrange_offset1_ = offset;
534   }
SetSubrange2(int offset,int len)535   void SetSubrange2(int offset, int len) {
536     subrange_offset2_ = offset;
537   }
538 
GetResult()539   Handle<JSArray> GetResult() {
540     return array_writer_.GetResult();
541   }
542 
543  private:
544   static const int CHUNK_LEN_LIMIT = 800;
545 
546   CompareOutputArrayWriter array_writer_;
547   LineEndsWrapper line_ends1_;
548   LineEndsWrapper line_ends2_;
549   Handle<String> s1_;
550   Handle<String> s2_;
551   int subrange_offset1_;
552   int subrange_offset2_;
553 };
554 
555 
CompareStrings(Handle<String> s1,Handle<String> s2)556 Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
557                                          Handle<String> s2) {
558   s1 = String::Flatten(s1);
559   s2 = String::Flatten(s2);
560 
561   LineEndsWrapper line_ends1(s1);
562   LineEndsWrapper line_ends2(s2);
563 
564   LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
565   TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
566 
567   NarrowDownInput(&input, &output);
568 
569   Comparator::CalculateDifference(&input, &output);
570 
571   return output.GetResult();
572 }
573 
574 
575 // Unwraps JSValue object, returning its field "value"
UnwrapJSValue(Handle<JSValue> jsValue)576 static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
577   return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
578 }
579 
580 
581 // Wraps any object into a OpaqueReference, that will hide the object
582 // from JavaScript.
WrapInJSValue(Handle<HeapObject> object)583 static Handle<JSValue> WrapInJSValue(Handle<HeapObject> object) {
584   Isolate* isolate = object->GetIsolate();
585   Handle<JSFunction> constructor = isolate->opaque_reference_function();
586   Handle<JSValue> result =
587       Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
588   result->set_value(*object);
589   return result;
590 }
591 
592 
UnwrapSharedFunctionInfoFromJSValue(Handle<JSValue> jsValue)593 static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
594     Handle<JSValue> jsValue) {
595   Object* shared = jsValue->value();
596   CHECK(shared->IsSharedFunctionInfo());
597   return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
598 }
599 
600 
GetArrayLength(Handle<JSArray> array)601 static int GetArrayLength(Handle<JSArray> array) {
602   Object* length = array->length();
603   CHECK(length->IsSmi());
604   return Smi::cast(length)->value();
605 }
606 
607 
SetInitialProperties(Handle<String> name,int start_position,int end_position,int param_num,int literal_count,int parent_index)608 void FunctionInfoWrapper::SetInitialProperties(Handle<String> name,
609                                                int start_position,
610                                                int end_position, int param_num,
611                                                int literal_count,
612                                                int parent_index) {
613   HandleScope scope(isolate());
614   this->SetField(kFunctionNameOffset_, name);
615   this->SetSmiValueField(kStartPositionOffset_, start_position);
616   this->SetSmiValueField(kEndPositionOffset_, end_position);
617   this->SetSmiValueField(kParamNumOffset_, param_num);
618   this->SetSmiValueField(kLiteralNumOffset_, literal_count);
619   this->SetSmiValueField(kParentIndexOffset_, parent_index);
620 }
621 
SetSharedFunctionInfo(Handle<SharedFunctionInfo> info)622 void FunctionInfoWrapper::SetSharedFunctionInfo(
623     Handle<SharedFunctionInfo> info) {
624   Handle<JSValue> info_holder = WrapInJSValue(info);
625   this->SetField(kSharedFunctionInfoOffset_, info_holder);
626 }
627 
GetSharedFunctionInfo()628 Handle<SharedFunctionInfo> FunctionInfoWrapper::GetSharedFunctionInfo() {
629   Handle<Object> element = this->GetField(kSharedFunctionInfoOffset_);
630   Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
631   Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
632   CHECK(raw_result->IsSharedFunctionInfo());
633   return Handle<SharedFunctionInfo>::cast(raw_result);
634 }
635 
SetProperties(Handle<String> name,int start_position,int end_position,Handle<SharedFunctionInfo> info)636 void SharedInfoWrapper::SetProperties(Handle<String> name,
637                                       int start_position,
638                                       int end_position,
639                                       Handle<SharedFunctionInfo> info) {
640   HandleScope scope(isolate());
641   this->SetField(kFunctionNameOffset_, name);
642   Handle<JSValue> info_holder = WrapInJSValue(info);
643   this->SetField(kSharedInfoOffset_, info_holder);
644   this->SetSmiValueField(kStartPositionOffset_, start_position);
645   this->SetSmiValueField(kEndPositionOffset_, end_position);
646 }
647 
648 
GetInfo()649 Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() {
650   Handle<Object> element = this->GetField(kSharedInfoOffset_);
651   Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
652   return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
653 }
654 
655 
InitializeThreadLocal(Debug * debug)656 void LiveEdit::InitializeThreadLocal(Debug* debug) {
657   debug->thread_local_.frame_drop_mode_ = LIVE_EDIT_FRAMES_UNTOUCHED;
658 }
659 
660 
SetAfterBreakTarget(Debug * debug)661 bool LiveEdit::SetAfterBreakTarget(Debug* debug) {
662   Code* code = NULL;
663   Isolate* isolate = debug->isolate_;
664   switch (debug->thread_local_.frame_drop_mode_) {
665     case LIVE_EDIT_FRAMES_UNTOUCHED:
666       return false;
667     case LIVE_EDIT_FRAME_DROPPED_IN_DEBUG_SLOT_CALL:
668       // Debug break slot stub does not return normally, instead it manually
669       // cleans the stack and jumps. We should patch the jump address.
670       code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
671       break;
672     case LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL:
673       // Nothing to do, after_break_target is not used here.
674       return true;
675     case LIVE_EDIT_FRAME_DROPPED_IN_RETURN_CALL:
676       code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
677       break;
678     case LIVE_EDIT_CURRENTLY_SET_MODE:
679       UNREACHABLE();
680       break;
681   }
682   debug->after_break_target_ = code->entry();
683   return true;
684 }
685 
686 
GatherCompileInfo(Handle<Script> script,Handle<String> source)687 MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script,
688                                                  Handle<String> source) {
689   Isolate* isolate = script->GetIsolate();
690 
691   MaybeHandle<JSArray> infos;
692   Handle<Object> original_source =
693       Handle<Object>(script->source(), isolate);
694   script->set_source(*source);
695 
696   {
697     // Creating verbose TryCatch from public API is currently the only way to
698     // force code save location. We do not use this the object directly.
699     v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
700     try_catch.SetVerbose(true);
701 
702     // A logical 'try' section.
703     infos = Compiler::CompileForLiveEdit(script);
704   }
705 
706   // A logical 'catch' section.
707   Handle<JSObject> rethrow_exception;
708   if (isolate->has_pending_exception()) {
709     Handle<Object> exception(isolate->pending_exception(), isolate);
710     MessageLocation message_location = isolate->GetMessageLocation();
711 
712     isolate->clear_pending_message();
713     isolate->clear_pending_exception();
714 
715     // If possible, copy positions from message object to exception object.
716     if (exception->IsJSObject() && !message_location.script().is_null()) {
717       rethrow_exception = Handle<JSObject>::cast(exception);
718 
719       Factory* factory = isolate->factory();
720       Handle<String> start_pos_key = factory->InternalizeOneByteString(
721           STATIC_CHAR_VECTOR("startPosition"));
722       Handle<String> end_pos_key =
723           factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("endPosition"));
724       Handle<String> script_obj_key =
725           factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptObject"));
726       Handle<Smi> start_pos(
727           Smi::FromInt(message_location.start_pos()), isolate);
728       Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
729       Handle<JSObject> script_obj =
730           Script::GetWrapper(message_location.script());
731       Object::SetProperty(rethrow_exception, start_pos_key, start_pos, SLOPPY)
732           .Assert();
733       Object::SetProperty(rethrow_exception, end_pos_key, end_pos, SLOPPY)
734           .Assert();
735       Object::SetProperty(rethrow_exception, script_obj_key, script_obj, SLOPPY)
736           .Assert();
737     }
738   }
739 
740   // A logical 'finally' section.
741   script->set_source(*original_source);
742 
743   if (rethrow_exception.is_null()) {
744     return infos.ToHandleChecked();
745   } else {
746     return isolate->Throw<JSArray>(rethrow_exception);
747   }
748 }
749 
750 
751 // Visitor that finds all references to a particular code object,
752 // including "CODE_TARGET" references in other code objects and replaces
753 // them on the fly.
754 class ReplacingVisitor : public ObjectVisitor {
755  public:
ReplacingVisitor(Code * original,Code * substitution)756   explicit ReplacingVisitor(Code* original, Code* substitution)
757     : original_(original), substitution_(substitution) {
758   }
759 
VisitPointers(Object ** start,Object ** end)760   void VisitPointers(Object** start, Object** end) override {
761     for (Object** p = start; p < end; p++) {
762       if (*p == original_) {
763         *p = substitution_;
764       }
765     }
766   }
767 
VisitCodeEntry(Address entry)768   void VisitCodeEntry(Address entry) override {
769     if (Code::GetObjectFromEntryAddress(entry) == original_) {
770       Address substitution_entry = substitution_->instruction_start();
771       Memory::Address_at(entry) = substitution_entry;
772     }
773   }
774 
VisitCodeTarget(RelocInfo * rinfo)775   void VisitCodeTarget(RelocInfo* rinfo) override {
776     if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
777         Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
778       Address substitution_entry = substitution_->instruction_start();
779       rinfo->set_target_address(substitution_entry);
780     }
781   }
782 
VisitDebugTarget(RelocInfo * rinfo)783   void VisitDebugTarget(RelocInfo* rinfo) override { VisitCodeTarget(rinfo); }
784 
785  private:
786   Code* original_;
787   Code* substitution_;
788 };
789 
790 
791 // Finds all references to original and replaces them with substitution.
ReplaceCodeObject(Handle<Code> original,Handle<Code> substitution)792 static void ReplaceCodeObject(Handle<Code> original,
793                               Handle<Code> substitution) {
794   // Perform a full GC in order to ensure that we are not in the middle of an
795   // incremental marking phase when we are replacing the code object.
796   // Since we are not in an incremental marking phase we can write pointers
797   // to code objects (that are never in new space) without worrying about
798   // write barriers.
799   Heap* heap = original->GetHeap();
800   HeapIterator iterator(heap);
801 
802   DCHECK(!heap->InNewSpace(*substitution));
803 
804   ReplacingVisitor visitor(*original, *substitution);
805 
806   // Iterate over all roots. Stack frames may have pointer into original code,
807   // so temporary replace the pointers with offset numbers
808   // in prologue/epilogue.
809   heap->IterateRoots(&visitor, VISIT_ALL);
810 
811   // Now iterate over all pointers of all objects, including code_target
812   // implicit pointers.
813   for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
814     obj->Iterate(&visitor);
815   }
816 }
817 
818 
819 // Patch function literals.
820 // Name 'literals' is a misnomer. Rather it's a cache for complex object
821 // boilerplates and for a native context. We must clean cached values.
822 // Additionally we may need to allocate a new array if number of literals
823 // changed.
824 class LiteralFixer {
825  public:
PatchLiterals(FunctionInfoWrapper * compile_info_wrapper,Handle<SharedFunctionInfo> shared_info,bool feedback_metadata_changed,Isolate * isolate)826   static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
827                             Handle<SharedFunctionInfo> shared_info,
828                             bool feedback_metadata_changed, Isolate* isolate) {
829     int new_literal_count = compile_info_wrapper->GetLiteralCount();
830     int old_literal_count = shared_info->num_literals();
831 
832     if (old_literal_count == new_literal_count && !feedback_metadata_changed) {
833       // If literal count didn't change, simply go over all functions
834       // and clear literal arrays.
835       ClearValuesVisitor visitor;
836       IterateJSFunctions(shared_info, &visitor);
837     } else {
838       // When literal count changes, we have to create new array instances.
839       // Since we cannot create instances when iterating heap, we should first
840       // collect all functions and fix their literal arrays.
841       Handle<FixedArray> function_instances =
842           CollectJSFunctions(shared_info, isolate);
843       Handle<TypeFeedbackMetadata> feedback_metadata(
844           shared_info->feedback_metadata());
845 
846       for (int i = 0; i < function_instances->length(); i++) {
847         Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
848         Handle<TypeFeedbackVector> vector =
849             TypeFeedbackVector::New(isolate, feedback_metadata);
850         Handle<LiteralsArray> new_literals =
851             LiteralsArray::New(isolate, vector, new_literal_count);
852         fun->set_literals(*new_literals);
853       }
854 
855       shared_info->set_num_literals(new_literal_count);
856     }
857   }
858 
859  private:
860   // Iterates all function instances in the HEAP that refers to the
861   // provided shared_info.
862   template<typename Visitor>
IterateJSFunctions(Handle<SharedFunctionInfo> shared_info,Visitor * visitor)863   static void IterateJSFunctions(Handle<SharedFunctionInfo> shared_info,
864                                  Visitor* visitor) {
865     HeapIterator iterator(shared_info->GetHeap());
866     for (HeapObject* obj = iterator.next(); obj != NULL;
867         obj = iterator.next()) {
868       if (obj->IsJSFunction()) {
869         JSFunction* function = JSFunction::cast(obj);
870         if (function->shared() == *shared_info) {
871           visitor->visit(function);
872         }
873       }
874     }
875   }
876 
877   // Finds all instances of JSFunction that refers to the provided shared_info
878   // and returns array with them.
CollectJSFunctions(Handle<SharedFunctionInfo> shared_info,Isolate * isolate)879   static Handle<FixedArray> CollectJSFunctions(
880       Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
881     CountVisitor count_visitor;
882     count_visitor.count = 0;
883     IterateJSFunctions(shared_info, &count_visitor);
884     int size = count_visitor.count;
885 
886     Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
887     if (size > 0) {
888       CollectVisitor collect_visitor(result);
889       IterateJSFunctions(shared_info, &collect_visitor);
890     }
891     return result;
892   }
893 
894   class ClearValuesVisitor {
895    public:
visit(JSFunction * fun)896     void visit(JSFunction* fun) {
897       LiteralsArray* literals = fun->literals();
898       int len = literals->literals_count();
899       for (int j = 0; j < len; j++) {
900         literals->set_literal_undefined(j);
901       }
902     }
903   };
904 
905   class CountVisitor {
906    public:
visit(JSFunction * fun)907     void visit(JSFunction* fun) {
908       count++;
909     }
910     int count;
911   };
912 
913   class CollectVisitor {
914    public:
CollectVisitor(Handle<FixedArray> output)915     explicit CollectVisitor(Handle<FixedArray> output)
916         : m_output(output), m_pos(0) {}
917 
visit(JSFunction * fun)918     void visit(JSFunction* fun) {
919       m_output->set(m_pos, fun);
920       m_pos++;
921     }
922    private:
923     Handle<FixedArray> m_output;
924     int m_pos;
925   };
926 };
927 
928 
929 // Marks code that shares the same shared function info or has inlined
930 // code that shares the same function info.
931 class DependentFunctionMarker: public OptimizedFunctionVisitor {
932  public:
933   SharedFunctionInfo* shared_info_;
934   bool found_;
935 
DependentFunctionMarker(SharedFunctionInfo * shared_info)936   explicit DependentFunctionMarker(SharedFunctionInfo* shared_info)
937     : shared_info_(shared_info), found_(false) { }
938 
EnterContext(Context * context)939   virtual void EnterContext(Context* context) { }  // Don't care.
LeaveContext(Context * context)940   virtual void LeaveContext(Context* context)  { }  // Don't care.
VisitFunction(JSFunction * function)941   virtual void VisitFunction(JSFunction* function) {
942     // It should be guaranteed by the iterator that everything is optimized.
943     DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
944     if (function->Inlines(shared_info_)) {
945       // Mark the code for deoptimization.
946       function->code()->set_marked_for_deoptimization(true);
947       found_ = true;
948     }
949   }
950 };
951 
952 
DeoptimizeDependentFunctions(SharedFunctionInfo * function_info)953 static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
954   DisallowHeapAllocation no_allocation;
955   DependentFunctionMarker marker(function_info);
956   // TODO(titzer): need to traverse all optimized code to find OSR code here.
957   Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker);
958 
959   if (marker.found_) {
960     // Only go through with the deoptimization if something was found.
961     Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate());
962   }
963 }
964 
965 
ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,Handle<JSArray> shared_info_array)966 void LiveEdit::ReplaceFunctionCode(
967     Handle<JSArray> new_compile_info_array,
968     Handle<JSArray> shared_info_array) {
969   Isolate* isolate = new_compile_info_array->GetIsolate();
970 
971   FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
972   SharedInfoWrapper shared_info_wrapper(shared_info_array);
973 
974   Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
975   Handle<SharedFunctionInfo> new_shared_info =
976       compile_info_wrapper.GetSharedFunctionInfo();
977   bool feedback_metadata_changed = false;
978 
979   if (shared_info->is_compiled()) {
980     // Take whatever code we can get from the new shared function info. We
981     // expect activations of neither the old bytecode nor old FCG code, since
982     // the lowest activation is going to be restarted.
983     Handle<Code> old_code(shared_info->code());
984     Handle<Code> new_code(new_shared_info->code());
985     // Clear old bytecode. This will trigger self-healing if we do not install
986     // new bytecode.
987     shared_info->ClearBytecodeArray();
988     if (!shared_info->HasBaselineCode()) {
989       // Every function from this SFI is interpreted.
990       if (!new_shared_info->HasBaselineCode()) {
991         // We have newly compiled bytecode. Simply replace the old one.
992         shared_info->set_bytecode_array(new_shared_info->bytecode_array());
993       } else {
994         // Rely on self-healing for places that used to run bytecode.
995         shared_info->ReplaceCode(*new_code);
996       }
997     } else {
998       // Functions from this SFI can be either interpreted or running FCG.
999       DCHECK(old_code->kind() == Code::FUNCTION);
1000       if (new_shared_info->HasBytecodeArray()) {
1001         // Start using new bytecode everywhere.
1002         shared_info->set_bytecode_array(new_shared_info->bytecode_array());
1003         ReplaceCodeObject(old_code,
1004                           isolate->builtins()->InterpreterEntryTrampoline());
1005       } else {
1006         // Start using new FCG code everywhere.
1007         // Rely on self-healing for places that used to run bytecode.
1008         DCHECK(new_code->kind() == Code::FUNCTION);
1009         ReplaceCodeObject(old_code, new_code);
1010       }
1011     }
1012 
1013     if (shared_info->HasDebugInfo()) {
1014       // Existing break points will be re-applied. Reset the debug info here.
1015       isolate->debug()->RemoveDebugInfoAndClearFromShared(
1016           handle(shared_info->GetDebugInfo()));
1017     }
1018     shared_info->set_scope_info(new_shared_info->scope_info());
1019     shared_info->set_outer_scope_info(new_shared_info->outer_scope_info());
1020     shared_info->DisableOptimization(kLiveEdit);
1021     // Update the type feedback vector, if needed.
1022     Handle<TypeFeedbackMetadata> new_feedback_metadata(
1023         new_shared_info->feedback_metadata());
1024     feedback_metadata_changed =
1025         new_feedback_metadata->DiffersFrom(shared_info->feedback_metadata());
1026     shared_info->set_feedback_metadata(*new_feedback_metadata);
1027   }
1028 
1029   int start_position = compile_info_wrapper.GetStartPosition();
1030   int end_position = compile_info_wrapper.GetEndPosition();
1031   shared_info->set_start_position(start_position);
1032   shared_info->set_end_position(end_position);
1033 
1034   LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info,
1035                               feedback_metadata_changed, isolate);
1036 
1037   DeoptimizeDependentFunctions(*shared_info);
1038   isolate->compilation_cache()->Remove(shared_info);
1039 }
1040 
1041 
FunctionSourceUpdated(Handle<JSArray> shared_info_array)1042 void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array) {
1043   SharedInfoWrapper shared_info_wrapper(shared_info_array);
1044   Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1045 
1046   DeoptimizeDependentFunctions(*shared_info);
1047   shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info);
1048 }
1049 
1050 
SetFunctionScript(Handle<JSValue> function_wrapper,Handle<Object> script_handle)1051 void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
1052                                  Handle<Object> script_handle) {
1053   Handle<SharedFunctionInfo> shared_info =
1054       UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
1055   Isolate* isolate = function_wrapper->GetIsolate();
1056   CHECK(script_handle->IsScript() || script_handle->IsUndefined(isolate));
1057   SharedFunctionInfo::SetScript(shared_info, script_handle);
1058   shared_info->DisableOptimization(kLiveEdit);
1059 
1060   function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info);
1061 }
1062 
1063 namespace {
1064 // For a script text change (defined as position_change_array), translates
1065 // position in unchanged text to position in changed text.
1066 // Text change is a set of non-overlapping regions in text, that have changed
1067 // their contents and length. It is specified as array of groups of 3 numbers:
1068 // (change_begin, change_end, change_end_new_position).
1069 // Each group describes a change in text; groups are sorted by change_begin.
1070 // Only position in text beyond any changes may be successfully translated.
1071 // If a positions is inside some region that changed, result is currently
1072 // undefined.
TranslatePosition(int original_position,Handle<JSArray> position_change_array)1073 static int TranslatePosition(int original_position,
1074                              Handle<JSArray> position_change_array) {
1075   int position_diff = 0;
1076   int array_len = GetArrayLength(position_change_array);
1077   Isolate* isolate = position_change_array->GetIsolate();
1078   // TODO(635): binary search may be used here
1079   for (int i = 0; i < array_len; i += 3) {
1080     HandleScope scope(isolate);
1081     Handle<Object> element =
1082         JSReceiver::GetElement(isolate, position_change_array, i)
1083             .ToHandleChecked();
1084     CHECK(element->IsSmi());
1085     int chunk_start = Handle<Smi>::cast(element)->value();
1086     if (original_position < chunk_start) {
1087       break;
1088     }
1089     element = JSReceiver::GetElement(isolate, position_change_array, i + 1)
1090                   .ToHandleChecked();
1091     CHECK(element->IsSmi());
1092     int chunk_end = Handle<Smi>::cast(element)->value();
1093     // Position mustn't be inside a chunk.
1094     DCHECK(original_position >= chunk_end);
1095     element = JSReceiver::GetElement(isolate, position_change_array, i + 2)
1096                   .ToHandleChecked();
1097     CHECK(element->IsSmi());
1098     int chunk_changed_end = Handle<Smi>::cast(element)->value();
1099     position_diff = chunk_changed_end - chunk_end;
1100   }
1101 
1102   return original_position + position_diff;
1103 }
1104 
TranslateSourcePositionTable(Handle<AbstractCode> code,Handle<JSArray> position_change_array)1105 void TranslateSourcePositionTable(Handle<AbstractCode> code,
1106                                   Handle<JSArray> position_change_array) {
1107   Isolate* isolate = code->GetIsolate();
1108   Zone zone(isolate->allocator(), ZONE_NAME);
1109   SourcePositionTableBuilder builder(&zone);
1110 
1111   Handle<ByteArray> source_position_table(code->source_position_table());
1112   for (SourcePositionTableIterator iterator(*source_position_table);
1113        !iterator.done(); iterator.Advance()) {
1114     SourcePosition position = iterator.source_position();
1115     position.SetScriptOffset(
1116         TranslatePosition(position.ScriptOffset(), position_change_array));
1117     builder.AddPosition(iterator.code_offset(), position,
1118                         iterator.is_statement());
1119   }
1120 
1121   Handle<ByteArray> new_source_position_table(
1122       builder.ToSourcePositionTable(isolate, code));
1123   code->set_source_position_table(*new_source_position_table);
1124 }
1125 }  // namespace
1126 
PatchFunctionPositions(Handle<JSArray> shared_info_array,Handle<JSArray> position_change_array)1127 void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
1128                                       Handle<JSArray> position_change_array) {
1129   SharedInfoWrapper shared_info_wrapper(shared_info_array);
1130   Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
1131 
1132   int old_function_start = info->start_position();
1133   int new_function_start = TranslatePosition(old_function_start,
1134                                              position_change_array);
1135   int new_function_end = TranslatePosition(info->end_position(),
1136                                            position_change_array);
1137   int new_function_token_pos =
1138       TranslatePosition(info->function_token_position(), position_change_array);
1139 
1140   info->set_start_position(new_function_start);
1141   info->set_end_position(new_function_end);
1142   info->set_function_token_position(new_function_token_pos);
1143 
1144   if (info->HasBytecodeArray()) {
1145     TranslateSourcePositionTable(
1146         Handle<AbstractCode>(AbstractCode::cast(info->bytecode_array())),
1147         position_change_array);
1148   }
1149   if (info->code()->kind() == Code::FUNCTION) {
1150     TranslateSourcePositionTable(
1151         Handle<AbstractCode>(AbstractCode::cast(info->code())),
1152         position_change_array);
1153   }
1154   if (info->HasDebugInfo()) {
1155     // Existing break points will be re-applied. Reset the debug info here.
1156     info->GetIsolate()->debug()->RemoveDebugInfoAndClearFromShared(
1157         handle(info->GetDebugInfo()));
1158   }
1159 }
1160 
1161 
CreateScriptCopy(Handle<Script> original)1162 static Handle<Script> CreateScriptCopy(Handle<Script> original) {
1163   Isolate* isolate = original->GetIsolate();
1164 
1165   Handle<String> original_source(String::cast(original->source()));
1166   Handle<Script> copy = isolate->factory()->NewScript(original_source);
1167 
1168   copy->set_name(original->name());
1169   copy->set_line_offset(original->line_offset());
1170   copy->set_column_offset(original->column_offset());
1171   copy->set_type(original->type());
1172   copy->set_context_data(original->context_data());
1173   copy->set_eval_from_shared(original->eval_from_shared());
1174   copy->set_eval_from_position(original->eval_from_position());
1175 
1176   // Copy all the flags, but clear compilation state.
1177   copy->set_flags(original->flags());
1178   copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL);
1179 
1180   return copy;
1181 }
1182 
1183 
ChangeScriptSource(Handle<Script> original_script,Handle<String> new_source,Handle<Object> old_script_name)1184 Handle<Object> LiveEdit::ChangeScriptSource(Handle<Script> original_script,
1185                                             Handle<String> new_source,
1186                                             Handle<Object> old_script_name) {
1187   Isolate* isolate = original_script->GetIsolate();
1188   Handle<Object> old_script_object;
1189   if (old_script_name->IsString()) {
1190     Handle<Script> old_script = CreateScriptCopy(original_script);
1191     old_script->set_name(String::cast(*old_script_name));
1192     old_script_object = old_script;
1193     isolate->debug()->OnAfterCompile(old_script);
1194   } else {
1195     old_script_object = isolate->factory()->null_value();
1196   }
1197 
1198   original_script->set_source(*new_source);
1199 
1200   // Drop line ends so that they will be recalculated.
1201   original_script->set_line_ends(isolate->heap()->undefined_value());
1202 
1203   return old_script_object;
1204 }
1205 
1206 
1207 
ReplaceRefToNestedFunction(Handle<JSValue> parent_function_wrapper,Handle<JSValue> orig_function_wrapper,Handle<JSValue> subst_function_wrapper)1208 void LiveEdit::ReplaceRefToNestedFunction(
1209     Handle<JSValue> parent_function_wrapper,
1210     Handle<JSValue> orig_function_wrapper,
1211     Handle<JSValue> subst_function_wrapper) {
1212 
1213   Handle<SharedFunctionInfo> parent_shared =
1214       UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
1215   Handle<SharedFunctionInfo> orig_shared =
1216       UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
1217   Handle<SharedFunctionInfo> subst_shared =
1218       UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
1219 
1220   for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
1221     if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
1222       if (it.rinfo()->target_object() == *orig_shared) {
1223         it.rinfo()->set_target_object(*subst_shared);
1224       }
1225     }
1226   }
1227 }
1228 
1229 
1230 // Check an activation against list of functions. If there is a function
1231 // that matches, its status in result array is changed to status argument value.
CheckActivation(Handle<JSArray> shared_info_array,Handle<JSArray> result,StackFrame * frame,LiveEdit::FunctionPatchabilityStatus status)1232 static bool CheckActivation(Handle<JSArray> shared_info_array,
1233                             Handle<JSArray> result,
1234                             StackFrame* frame,
1235                             LiveEdit::FunctionPatchabilityStatus status) {
1236   if (!frame->is_java_script()) return false;
1237 
1238   Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function());
1239 
1240   Isolate* isolate = shared_info_array->GetIsolate();
1241   int len = GetArrayLength(shared_info_array);
1242   for (int i = 0; i < len; i++) {
1243     HandleScope scope(isolate);
1244     Handle<Object> element =
1245         JSReceiver::GetElement(isolate, shared_info_array, i).ToHandleChecked();
1246     Handle<JSValue> jsvalue = Handle<JSValue>::cast(element);
1247     Handle<SharedFunctionInfo> shared =
1248         UnwrapSharedFunctionInfoFromJSValue(jsvalue);
1249 
1250     if (function->Inlines(*shared)) {
1251       SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
1252       return true;
1253     }
1254   }
1255   return false;
1256 }
1257 
1258 
1259 // Iterates over handler chain and removes all elements that are inside
1260 // frames being dropped.
FixTryCatchHandler(StackFrame * top_frame,StackFrame * bottom_frame)1261 static bool FixTryCatchHandler(StackFrame* top_frame,
1262                                StackFrame* bottom_frame) {
1263   Address* pointer_address =
1264       &Memory::Address_at(top_frame->isolate()->get_address_from_id(
1265           Isolate::kHandlerAddress));
1266 
1267   while (*pointer_address < top_frame->sp()) {
1268     pointer_address = &Memory::Address_at(*pointer_address);
1269   }
1270   Address* above_frame_address = pointer_address;
1271   while (*pointer_address < bottom_frame->fp()) {
1272     pointer_address = &Memory::Address_at(*pointer_address);
1273   }
1274   bool change = *above_frame_address != *pointer_address;
1275   *above_frame_address = *pointer_address;
1276   return change;
1277 }
1278 
1279 
1280 // Initializes an artificial stack frame. The data it contains is used for:
1281 //  a. successful work of frame dropper code which eventually gets control,
1282 //  b. being compatible with a typed frame structure for various stack
1283 //     iterators.
1284 // Frame structure (conforms to InternalFrame structure):
1285 //   -- function
1286 //   -- code
1287 //   -- SMI marker
1288 //   -- frame base
SetUpFrameDropperFrame(StackFrame * bottom_js_frame,Handle<Code> code)1289 static void SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
1290                                    Handle<Code> code) {
1291   DCHECK(bottom_js_frame->is_java_script());
1292   Address fp = bottom_js_frame->fp();
1293   Memory::Object_at(fp + FrameDropperFrameConstants::kFunctionOffset) =
1294       Memory::Object_at(fp + StandardFrameConstants::kFunctionOffset);
1295   Memory::Object_at(fp + FrameDropperFrameConstants::kFrameTypeOffset) =
1296       Smi::FromInt(StackFrame::INTERNAL);
1297   Memory::Object_at(fp + FrameDropperFrameConstants::kCodeOffset) = *code;
1298 }
1299 
1300 
1301 // Removes specified range of frames from stack. There may be 1 or more
1302 // frames in range. Anyway the bottom frame is restarted rather than dropped,
1303 // and therefore has to be a JavaScript frame.
1304 // Returns error message or NULL.
DropFrames(Vector<StackFrame * > frames,int top_frame_index,int bottom_js_frame_index,LiveEditFrameDropMode * mode)1305 static const char* DropFrames(Vector<StackFrame*> frames, int top_frame_index,
1306                               int bottom_js_frame_index,
1307                               LiveEditFrameDropMode* mode) {
1308   if (!LiveEdit::kFrameDropperSupported) {
1309     return "Stack manipulations are not supported in this architecture.";
1310   }
1311 
1312   StackFrame* pre_top_frame = frames[top_frame_index - 1];
1313   StackFrame* top_frame = frames[top_frame_index];
1314   StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
1315 
1316   DCHECK(bottom_js_frame->is_java_script());
1317 
1318   // Check the nature of the top frame.
1319   Isolate* isolate = bottom_js_frame->isolate();
1320   Code* pre_top_frame_code = pre_top_frame->LookupCode();
1321   bool frame_has_padding = true;
1322   if (pre_top_frame_code ==
1323       isolate->builtins()->builtin(Builtins::kSlot_DebugBreak)) {
1324     // OK, we can drop debug break slot.
1325     *mode = LIVE_EDIT_FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
1326   } else if (pre_top_frame_code ==
1327              isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)) {
1328     // OK, we can drop our own code.
1329     pre_top_frame = frames[top_frame_index - 2];
1330     top_frame = frames[top_frame_index - 1];
1331     *mode = LIVE_EDIT_CURRENTLY_SET_MODE;
1332     frame_has_padding = false;
1333   } else if (pre_top_frame_code ==
1334              isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
1335     *mode = LIVE_EDIT_FRAME_DROPPED_IN_RETURN_CALL;
1336   } else if (pre_top_frame_code->kind() == Code::STUB &&
1337              CodeStub::GetMajorKey(pre_top_frame_code) == CodeStub::CEntry) {
1338     // Entry from our unit tests on 'debugger' statement.
1339     // It's fine, we support this case.
1340     *mode = LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL;
1341     // We don't have a padding from 'debugger' statement call.
1342     // Here the stub is CEntry, it's not debug-only and can't be padded.
1343     // If anyone would complain, a proxy padded stub could be added.
1344     frame_has_padding = false;
1345   } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
1346     // This must be adaptor that remain from the frame dropping that
1347     // is still on stack. A frame dropper frame must be above it.
1348     DCHECK(frames[top_frame_index - 2]->LookupCode() ==
1349            isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
1350     pre_top_frame = frames[top_frame_index - 3];
1351     top_frame = frames[top_frame_index - 2];
1352     *mode = LIVE_EDIT_CURRENTLY_SET_MODE;
1353     frame_has_padding = false;
1354   } else if (pre_top_frame_code->kind() == Code::BYTECODE_HANDLER) {
1355     // Interpreted bytecode takes up two stack frames, one for the bytecode
1356     // handler and one for the interpreter entry trampoline. Therefore we shift
1357     // up by one frame.
1358     *mode = LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL;
1359     pre_top_frame = frames[top_frame_index - 2];
1360     top_frame = frames[top_frame_index - 1];
1361   } else {
1362     return "Unknown structure of stack above changing function";
1363   }
1364 
1365   Address unused_stack_top = top_frame->sp();
1366   Address unused_stack_bottom =
1367       bottom_js_frame->fp() - FrameDropperFrameConstants::kFixedFrameSize +
1368       2 * kPointerSize;  // Bigger address end is exclusive.
1369 
1370   Address* top_frame_pc_address = top_frame->pc_address();
1371 
1372   // top_frame may be damaged below this point. Do not used it.
1373   DCHECK(!(top_frame = NULL));
1374 
1375   if (unused_stack_top > unused_stack_bottom) {
1376     if (frame_has_padding) {
1377       int shortage_bytes =
1378           static_cast<int>(unused_stack_top - unused_stack_bottom);
1379 
1380       Address padding_start =
1381           pre_top_frame->fp() -
1382           (FrameDropperFrameConstants::kFixedFrameSize - kPointerSize);
1383 
1384       Address padding_pointer = padding_start;
1385       Smi* padding_object = Smi::FromInt(LiveEdit::kFramePaddingValue);
1386       while (Memory::Object_at(padding_pointer) == padding_object) {
1387         padding_pointer -= kPointerSize;
1388       }
1389       int padding_counter =
1390           Smi::cast(Memory::Object_at(padding_pointer))->value();
1391       if (padding_counter * kPointerSize < shortage_bytes) {
1392         return "Not enough space for frame dropper frame "
1393             "(even with padding frame)";
1394       }
1395       Memory::Object_at(padding_pointer) =
1396           Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
1397 
1398       StackFrame* pre_pre_frame = frames[top_frame_index - 2];
1399 
1400       MemMove(padding_start + kPointerSize - shortage_bytes,
1401               padding_start + kPointerSize,
1402               FrameDropperFrameConstants::kFixedFrameSize - kPointerSize);
1403 
1404       pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
1405       pre_pre_frame->SetCallerFp(pre_top_frame->fp());
1406       unused_stack_top -= shortage_bytes;
1407 
1408       STATIC_ASSERT(sizeof(Address) == kPointerSize);
1409       top_frame_pc_address -= shortage_bytes / kPointerSize;
1410     } else {
1411       return "Not enough space for frame dropper frame";
1412     }
1413   }
1414 
1415   // Committing now. After this point we should return only NULL value.
1416 
1417   FixTryCatchHandler(pre_top_frame, bottom_js_frame);
1418   // Make sure FixTryCatchHandler is idempotent.
1419   DCHECK(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
1420 
1421   Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit();
1422   *top_frame_pc_address = code->entry();
1423   pre_top_frame->SetCallerFp(bottom_js_frame->fp());
1424 
1425   SetUpFrameDropperFrame(bottom_js_frame, code);
1426 
1427   for (Address a = unused_stack_top;
1428       a < unused_stack_bottom;
1429       a += kPointerSize) {
1430     Memory::Object_at(a) = Smi::kZero;
1431   }
1432 
1433   return NULL;
1434 }
1435 
1436 
1437 // Describes a set of call frames that execute any of listed functions.
1438 // Finding no such frames does not mean error.
1439 class MultipleFunctionTarget {
1440  public:
MultipleFunctionTarget(Handle<JSArray> old_shared_array,Handle<JSArray> new_shared_array,Handle<JSArray> result)1441   MultipleFunctionTarget(Handle<JSArray> old_shared_array,
1442                          Handle<JSArray> new_shared_array,
1443                          Handle<JSArray> result)
1444       : old_shared_array_(old_shared_array),
1445         new_shared_array_(new_shared_array),
1446         result_(result) {}
MatchActivation(StackFrame * frame,LiveEdit::FunctionPatchabilityStatus status)1447   bool MatchActivation(StackFrame* frame,
1448       LiveEdit::FunctionPatchabilityStatus status) {
1449     return CheckActivation(old_shared_array_, result_, frame, status);
1450   }
GetNotFoundMessage() const1451   const char* GetNotFoundMessage() const {
1452     return NULL;
1453   }
FrameUsesNewTarget(StackFrame * frame)1454   bool FrameUsesNewTarget(StackFrame* frame) {
1455     if (!frame->is_java_script()) return false;
1456     JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame);
1457     Handle<SharedFunctionInfo> old_shared(jsframe->function()->shared());
1458     Isolate* isolate = old_shared->GetIsolate();
1459     int len = GetArrayLength(old_shared_array_);
1460     // Find corresponding new shared function info and return whether it
1461     // references new.target.
1462     for (int i = 0; i < len; i++) {
1463       HandleScope scope(isolate);
1464       Handle<Object> old_element =
1465           JSReceiver::GetElement(isolate, old_shared_array_, i)
1466               .ToHandleChecked();
1467       if (!old_shared.is_identical_to(UnwrapSharedFunctionInfoFromJSValue(
1468               Handle<JSValue>::cast(old_element)))) {
1469         continue;
1470       }
1471 
1472       Handle<Object> new_element =
1473           JSReceiver::GetElement(isolate, new_shared_array_, i)
1474               .ToHandleChecked();
1475       if (new_element->IsUndefined(isolate)) return false;
1476       Handle<SharedFunctionInfo> new_shared =
1477           UnwrapSharedFunctionInfoFromJSValue(
1478               Handle<JSValue>::cast(new_element));
1479       if (new_shared->scope_info()->HasNewTarget()) {
1480         SetElementSloppy(
1481             result_, i,
1482             Handle<Smi>(
1483                 Smi::FromInt(
1484                     LiveEdit::FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART),
1485                 isolate));
1486         return true;
1487       }
1488       return false;
1489     }
1490     return false;
1491   }
1492 
set_status(LiveEdit::FunctionPatchabilityStatus status)1493   void set_status(LiveEdit::FunctionPatchabilityStatus status) {
1494     Isolate* isolate = old_shared_array_->GetIsolate();
1495     int len = GetArrayLength(old_shared_array_);
1496     for (int i = 0; i < len; ++i) {
1497       Handle<Object> old_element =
1498           JSReceiver::GetElement(isolate, result_, i).ToHandleChecked();
1499       if (!old_element->IsSmi() ||
1500           Smi::cast(*old_element)->value() ==
1501               LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {
1502         SetElementSloppy(result_, i,
1503                          Handle<Smi>(Smi::FromInt(status), isolate));
1504       }
1505     }
1506   }
1507 
1508  private:
1509   Handle<JSArray> old_shared_array_;
1510   Handle<JSArray> new_shared_array_;
1511   Handle<JSArray> result_;
1512 };
1513 
1514 
1515 // Drops all call frame matched by target and all frames above them.
1516 template <typename TARGET>
DropActivationsInActiveThreadImpl(Isolate * isolate,TARGET & target,bool do_drop)1517 static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
1518                                                      TARGET& target,  // NOLINT
1519                                                      bool do_drop) {
1520   Debug* debug = isolate->debug();
1521   Zone zone(isolate->allocator(), ZONE_NAME);
1522   Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
1523 
1524 
1525   int top_frame_index = -1;
1526   int frame_index = 0;
1527   for (; frame_index < frames.length(); frame_index++) {
1528     StackFrame* frame = frames[frame_index];
1529     if (frame->id() == debug->break_frame_id()) {
1530       top_frame_index = frame_index;
1531       break;
1532     }
1533     if (target.MatchActivation(
1534             frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
1535       // We are still above break_frame. It is not a target frame,
1536       // it is a problem.
1537       return "Debugger mark-up on stack is not found";
1538     }
1539   }
1540 
1541   if (top_frame_index == -1) {
1542     // We haven't found break frame, but no function is blocking us anyway.
1543     return target.GetNotFoundMessage();
1544   }
1545 
1546   bool target_frame_found = false;
1547   int bottom_js_frame_index = top_frame_index;
1548   bool non_droppable_frame_found = false;
1549   LiveEdit::FunctionPatchabilityStatus non_droppable_reason;
1550 
1551   for (; frame_index < frames.length(); frame_index++) {
1552     StackFrame* frame = frames[frame_index];
1553     if (frame->is_exit() || frame->is_builtin_exit()) {
1554       non_droppable_frame_found = true;
1555       non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE;
1556       break;
1557     }
1558     if (frame->is_java_script()) {
1559       SharedFunctionInfo* shared =
1560           JavaScriptFrame::cast(frame)->function()->shared();
1561       if (IsResumableFunction(shared->kind())) {
1562         non_droppable_frame_found = true;
1563         non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR;
1564         break;
1565       }
1566     }
1567     if (target.MatchActivation(
1568             frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
1569       target_frame_found = true;
1570       bottom_js_frame_index = frame_index;
1571     }
1572   }
1573 
1574   if (non_droppable_frame_found) {
1575     // There is a C or generator frame on stack.  We can't drop C frames, and we
1576     // can't restart generators.  Check that there are no target frames below
1577     // them.
1578     for (; frame_index < frames.length(); frame_index++) {
1579       StackFrame* frame = frames[frame_index];
1580       if (frame->is_java_script()) {
1581         if (target.MatchActivation(frame, non_droppable_reason)) {
1582           // Fail.
1583           return NULL;
1584         }
1585         if (non_droppable_reason ==
1586                 LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR &&
1587             !target_frame_found) {
1588           // Fail.
1589           target.set_status(non_droppable_reason);
1590           return NULL;
1591         }
1592       }
1593     }
1594   }
1595 
1596   // We cannot restart a frame that uses new.target.
1597   if (target.FrameUsesNewTarget(frames[bottom_js_frame_index])) return NULL;
1598 
1599   if (!do_drop) {
1600     // We are in check-only mode.
1601     return NULL;
1602   }
1603 
1604   if (!target_frame_found) {
1605     // Nothing to drop.
1606     return target.GetNotFoundMessage();
1607   }
1608 
1609   LiveEditFrameDropMode drop_mode = LIVE_EDIT_FRAMES_UNTOUCHED;
1610   const char* error_message =
1611       DropFrames(frames, top_frame_index, bottom_js_frame_index, &drop_mode);
1612 
1613   if (error_message != NULL) {
1614     return error_message;
1615   }
1616 
1617   // Adjust break_frame after some frames has been dropped.
1618   StackFrame::Id new_id = StackFrame::NO_ID;
1619   for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
1620     if (frames[i]->type() == StackFrame::JAVA_SCRIPT ||
1621         frames[i]->type() == StackFrame::INTERPRETED) {
1622       new_id = frames[i]->id();
1623       break;
1624     }
1625   }
1626   debug->FramesHaveBeenDropped(new_id, drop_mode);
1627   return NULL;
1628 }
1629 
1630 
1631 // Fills result array with statuses of functions. Modifies the stack
1632 // removing all listed function if possible and if do_drop is true.
DropActivationsInActiveThread(Handle<JSArray> old_shared_array,Handle<JSArray> new_shared_array,Handle<JSArray> result,bool do_drop)1633 static const char* DropActivationsInActiveThread(
1634     Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
1635     Handle<JSArray> result, bool do_drop) {
1636   MultipleFunctionTarget target(old_shared_array, new_shared_array, result);
1637   Isolate* isolate = old_shared_array->GetIsolate();
1638 
1639   const char* message =
1640       DropActivationsInActiveThreadImpl(isolate, target, do_drop);
1641   if (message) {
1642     return message;
1643   }
1644 
1645   int array_len = GetArrayLength(old_shared_array);
1646 
1647   // Replace "blocked on active" with "replaced on active" status.
1648   for (int i = 0; i < array_len; i++) {
1649     Handle<Object> obj =
1650         JSReceiver::GetElement(isolate, result, i).ToHandleChecked();
1651     if (*obj == Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
1652       Handle<Object> replaced(
1653           Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
1654       SetElementSloppy(result, i, replaced);
1655     }
1656   }
1657   return NULL;
1658 }
1659 
1660 
FindActiveGenerators(Handle<FixedArray> shared_info_array,Handle<FixedArray> result,int len)1661 bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
1662                                     Handle<FixedArray> result,
1663                                     int len) {
1664   Isolate* isolate = shared_info_array->GetIsolate();
1665   bool found_suspended_activations = false;
1666 
1667   DCHECK_LE(len, result->length());
1668 
1669   FunctionPatchabilityStatus active = FUNCTION_BLOCKED_ACTIVE_GENERATOR;
1670 
1671   Heap* heap = isolate->heap();
1672   HeapIterator iterator(heap);
1673   HeapObject* obj = NULL;
1674   while ((obj = iterator.next()) != NULL) {
1675     if (!obj->IsJSGeneratorObject()) continue;
1676 
1677     JSGeneratorObject* gen = JSGeneratorObject::cast(obj);
1678     if (gen->is_closed()) continue;
1679 
1680     HandleScope scope(isolate);
1681 
1682     for (int i = 0; i < len; i++) {
1683       Handle<JSValue> jsvalue = Handle<JSValue>::cast(
1684           FixedArray::get(*shared_info_array, i, isolate));
1685       Handle<SharedFunctionInfo> shared =
1686           UnwrapSharedFunctionInfoFromJSValue(jsvalue);
1687 
1688       if (gen->function()->shared() == *shared) {
1689         result->set(i, Smi::FromInt(active));
1690         found_suspended_activations = true;
1691       }
1692     }
1693   }
1694 
1695   return found_suspended_activations;
1696 }
1697 
1698 
1699 class InactiveThreadActivationsChecker : public ThreadVisitor {
1700  public:
InactiveThreadActivationsChecker(Handle<JSArray> old_shared_array,Handle<JSArray> result)1701   InactiveThreadActivationsChecker(Handle<JSArray> old_shared_array,
1702                                    Handle<JSArray> result)
1703       : old_shared_array_(old_shared_array),
1704         result_(result),
1705         has_blocked_functions_(false) {}
VisitThread(Isolate * isolate,ThreadLocalTop * top)1706   void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
1707     for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
1708       has_blocked_functions_ |=
1709           CheckActivation(old_shared_array_, result_, it.frame(),
1710                           LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
1711     }
1712   }
HasBlockedFunctions()1713   bool HasBlockedFunctions() {
1714     return has_blocked_functions_;
1715   }
1716 
1717  private:
1718   Handle<JSArray> old_shared_array_;
1719   Handle<JSArray> result_;
1720   bool has_blocked_functions_;
1721 };
1722 
1723 
CheckAndDropActivations(Handle<JSArray> old_shared_array,Handle<JSArray> new_shared_array,bool do_drop)1724 Handle<JSArray> LiveEdit::CheckAndDropActivations(
1725     Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
1726     bool do_drop) {
1727   Isolate* isolate = old_shared_array->GetIsolate();
1728   int len = GetArrayLength(old_shared_array);
1729 
1730   DCHECK(old_shared_array->HasFastElements());
1731   Handle<FixedArray> old_shared_array_elements(
1732       FixedArray::cast(old_shared_array->elements()));
1733 
1734   Handle<JSArray> result = isolate->factory()->NewJSArray(len);
1735   result->set_length(Smi::FromInt(len));
1736   JSObject::EnsureWritableFastElements(result);
1737   Handle<FixedArray> result_elements =
1738       handle(FixedArray::cast(result->elements()), isolate);
1739 
1740   // Fill the default values.
1741   for (int i = 0; i < len; i++) {
1742     FunctionPatchabilityStatus status = FUNCTION_AVAILABLE_FOR_PATCH;
1743     result_elements->set(i, Smi::FromInt(status));
1744   }
1745 
1746   // Scan the heap for active generators -- those that are either currently
1747   // running (as we wouldn't want to restart them, because we don't know where
1748   // to restart them from) or suspended.  Fail if any one corresponds to the set
1749   // of functions being edited.
1750   if (FindActiveGenerators(old_shared_array_elements, result_elements, len)) {
1751     return result;
1752   }
1753 
1754   // Check inactive threads. Fail if some functions are blocked there.
1755   InactiveThreadActivationsChecker inactive_threads_checker(old_shared_array,
1756                                                             result);
1757   isolate->thread_manager()->IterateArchivedThreads(
1758       &inactive_threads_checker);
1759   if (inactive_threads_checker.HasBlockedFunctions()) {
1760     return result;
1761   }
1762 
1763   // Try to drop activations from the current stack.
1764   const char* error_message = DropActivationsInActiveThread(
1765       old_shared_array, new_shared_array, result, do_drop);
1766   if (error_message != NULL) {
1767     // Add error message as an array extra element.
1768     Handle<String> str =
1769         isolate->factory()->NewStringFromAsciiChecked(error_message);
1770     SetElementSloppy(result, len, str);
1771   }
1772   return result;
1773 }
1774 
1775 
1776 // Describes a single callframe a target. Not finding this frame
1777 // means an error.
1778 class SingleFrameTarget {
1779  public:
SingleFrameTarget(JavaScriptFrame * frame)1780   explicit SingleFrameTarget(JavaScriptFrame* frame)
1781       : m_frame(frame),
1782         m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
1783 
MatchActivation(StackFrame * frame,LiveEdit::FunctionPatchabilityStatus status)1784   bool MatchActivation(StackFrame* frame,
1785       LiveEdit::FunctionPatchabilityStatus status) {
1786     if (frame->fp() == m_frame->fp()) {
1787       m_saved_status = status;
1788       return true;
1789     }
1790     return false;
1791   }
GetNotFoundMessage() const1792   const char* GetNotFoundMessage() const {
1793     return "Failed to found requested frame";
1794   }
saved_status()1795   LiveEdit::FunctionPatchabilityStatus saved_status() {
1796     return m_saved_status;
1797   }
set_status(LiveEdit::FunctionPatchabilityStatus status)1798   void set_status(LiveEdit::FunctionPatchabilityStatus status) {
1799     m_saved_status = status;
1800   }
1801 
FrameUsesNewTarget(StackFrame * frame)1802   bool FrameUsesNewTarget(StackFrame* frame) {
1803     if (!frame->is_java_script()) return false;
1804     JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame);
1805     Handle<SharedFunctionInfo> shared(jsframe->function()->shared());
1806     return shared->scope_info()->HasNewTarget();
1807   }
1808 
1809  private:
1810   JavaScriptFrame* m_frame;
1811   LiveEdit::FunctionPatchabilityStatus m_saved_status;
1812 };
1813 
1814 
1815 // Finds a drops required frame and all frames above.
1816 // Returns error message or NULL.
RestartFrame(JavaScriptFrame * frame)1817 const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) {
1818   SingleFrameTarget target(frame);
1819 
1820   const char* result =
1821       DropActivationsInActiveThreadImpl(frame->isolate(), target, true);
1822   if (result != NULL) {
1823     return result;
1824   }
1825   if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
1826     return "Function is blocked under native code";
1827   }
1828   if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR) {
1829     return "Function is blocked under a generator activation";
1830   }
1831   return NULL;
1832 }
1833 
Collect(FunctionLiteral * node,Handle<Script> script,Zone * zone,Isolate * isolate)1834 Handle<JSArray> LiveEditFunctionTracker::Collect(FunctionLiteral* node,
1835                                                  Handle<Script> script,
1836                                                  Zone* zone, Isolate* isolate) {
1837   LiveEditFunctionTracker visitor(script, zone, isolate);
1838   visitor.VisitFunctionLiteral(node);
1839   return visitor.result_;
1840 }
1841 
LiveEditFunctionTracker(Handle<Script> script,Zone * zone,Isolate * isolate)1842 LiveEditFunctionTracker::LiveEditFunctionTracker(Handle<Script> script,
1843                                                  Zone* zone, Isolate* isolate)
1844     : AstTraversalVisitor<LiveEditFunctionTracker>(isolate) {
1845   current_parent_index_ = -1;
1846   isolate_ = isolate;
1847   len_ = 0;
1848   result_ = isolate->factory()->NewJSArray(10);
1849   script_ = script;
1850   zone_ = zone;
1851 }
1852 
VisitFunctionLiteral(FunctionLiteral * node)1853 void LiveEditFunctionTracker::VisitFunctionLiteral(FunctionLiteral* node) {
1854   // FunctionStarted is called in pre-order.
1855   FunctionStarted(node);
1856   // Recurse using the regular traversal.
1857   AstTraversalVisitor::VisitFunctionLiteral(node);
1858   // FunctionDone are called in post-order.
1859   // TODO(jgruber): If required, replace the (linear cost)
1860   // FindSharedFunctionInfo call with a more efficient implementation.
1861   Handle<SharedFunctionInfo> info =
1862       script_->FindSharedFunctionInfo(node).ToHandleChecked();
1863   FunctionDone(info, node->scope());
1864 }
1865 
FunctionStarted(FunctionLiteral * fun)1866 void LiveEditFunctionTracker::FunctionStarted(FunctionLiteral* fun) {
1867   HandleScope handle_scope(isolate_);
1868   FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate_);
1869   info.SetInitialProperties(fun->name(), fun->start_position(),
1870                             fun->end_position(), fun->parameter_count(),
1871                             fun->materialized_literal_count(),
1872                             current_parent_index_);
1873   current_parent_index_ = len_;
1874   SetElementSloppy(result_, len_, info.GetJSArray());
1875   len_++;
1876 }
1877 
1878 // Saves full information about a function: its code, its scope info
1879 // and a SharedFunctionInfo object.
FunctionDone(Handle<SharedFunctionInfo> shared,Scope * scope)1880 void LiveEditFunctionTracker::FunctionDone(Handle<SharedFunctionInfo> shared,
1881                                            Scope* scope) {
1882   HandleScope handle_scope(isolate_);
1883   FunctionInfoWrapper info = FunctionInfoWrapper::cast(
1884       *JSReceiver::GetElement(isolate_, result_, current_parent_index_)
1885            .ToHandleChecked());
1886   info.SetSharedFunctionInfo(shared);
1887 
1888   Handle<Object> scope_info_list = SerializeFunctionScope(scope);
1889   info.SetFunctionScopeInfo(scope_info_list);
1890 
1891   current_parent_index_ = info.GetParentIndex();
1892 }
1893 
SerializeFunctionScope(Scope * scope)1894 Handle<Object> LiveEditFunctionTracker::SerializeFunctionScope(Scope* scope) {
1895   Handle<JSArray> scope_info_list = isolate_->factory()->NewJSArray(10);
1896   int scope_info_length = 0;
1897 
1898   // Saves some description of scope. It stores name and indexes of
1899   // variables in the whole scope chain. Null-named slots delimit
1900   // scopes of this chain.
1901   Scope* current_scope = scope;
1902   while (current_scope != NULL) {
1903     HandleScope handle_scope(isolate_);
1904     for (Variable* var : *current_scope->locals()) {
1905       if (!var->IsContextSlot()) continue;
1906       int context_index = var->index() - Context::MIN_CONTEXT_SLOTS;
1907       int location = scope_info_length + context_index * 2;
1908       SetElementSloppy(scope_info_list, location, var->name());
1909       SetElementSloppy(scope_info_list, location + 1,
1910                        handle(Smi::FromInt(var->index()), isolate_));
1911     }
1912     scope_info_length += current_scope->ContextLocalCount() * 2;
1913     SetElementSloppy(scope_info_list, scope_info_length,
1914                      isolate_->factory()->null_value());
1915     scope_info_length++;
1916 
1917     current_scope = current_scope->outer_scope();
1918   }
1919 
1920   return scope_info_list;
1921 }
1922 
1923 }  // namespace internal
1924 }  // namespace v8
1925