• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 
29 #include "v8.h"
30 
31 #include "liveedit.h"
32 
33 #include "compilation-cache.h"
34 #include "compiler.h"
35 #include "debug.h"
36 #include "deoptimizer.h"
37 #include "global-handles.h"
38 #include "parser.h"
39 #include "scopeinfo.h"
40 #include "scopes.h"
41 #include "v8memory.h"
42 
43 namespace v8 {
44 namespace internal {
45 
46 
47 #ifdef ENABLE_DEBUGGER_SUPPORT
48 
49 
SetElementNonStrict(Handle<JSObject> object,uint32_t index,Handle<Object> value)50 void SetElementNonStrict(Handle<JSObject> object,
51                          uint32_t index,
52                          Handle<Object> value) {
53   // Ignore return value from SetElement. It can only be a failure if there
54   // are element setters causing exceptions and the debugger context has none
55   // of these.
56   Handle<Object> no_failure =
57       JSObject::SetElement(object, index, value, NONE, kNonStrictMode);
58   ASSERT(!no_failure.is_null());
59   USE(no_failure);
60 }
61 
62 // A simple implementation of dynamic programming algorithm. It solves
63 // the problem of finding the difference of 2 arrays. It uses a table of results
64 // of subproblems. Each cell contains a number together with 2-bit flag
65 // that helps building the chunk list.
66 class Differencer {
67  public:
Differencer(Comparator::Input * input)68   explicit Differencer(Comparator::Input* input)
69       : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
70     buffer_ = NewArray<int>(len1_ * len2_);
71   }
~Differencer()72   ~Differencer() {
73     DeleteArray(buffer_);
74   }
75 
Initialize()76   void Initialize() {
77     int array_size = len1_ * len2_;
78     for (int i = 0; i < array_size; i++) {
79       buffer_[i] = kEmptyCellValue;
80     }
81   }
82 
83   // Makes sure that result for the full problem is calculated and stored
84   // in the table together with flags showing a path through subproblems.
FillTable()85   void FillTable() {
86     CompareUpToTail(0, 0);
87   }
88 
SaveResult(Comparator::Output * chunk_writer)89   void SaveResult(Comparator::Output* chunk_writer) {
90     ResultWriter writer(chunk_writer);
91 
92     int pos1 = 0;
93     int pos2 = 0;
94     while (true) {
95       if (pos1 < len1_) {
96         if (pos2 < len2_) {
97           Direction dir = get_direction(pos1, pos2);
98           switch (dir) {
99             case EQ:
100               writer.eq();
101               pos1++;
102               pos2++;
103               break;
104             case SKIP1:
105               writer.skip1(1);
106               pos1++;
107               break;
108             case SKIP2:
109             case SKIP_ANY:
110               writer.skip2(1);
111               pos2++;
112               break;
113             default:
114               UNREACHABLE();
115           }
116         } else {
117           writer.skip1(len1_ - pos1);
118           break;
119         }
120       } else {
121         if (len2_ != pos2) {
122           writer.skip2(len2_ - pos2);
123         }
124         break;
125       }
126     }
127     writer.close();
128   }
129 
130  private:
131   Comparator::Input* input_;
132   int* buffer_;
133   int len1_;
134   int len2_;
135 
136   enum Direction {
137     EQ = 0,
138     SKIP1,
139     SKIP2,
140     SKIP_ANY,
141 
142     MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
143   };
144 
145   // Computes result for a subtask and optionally caches it in the buffer table.
146   // All results values are shifted to make space for flags in the lower bits.
CompareUpToTail(int pos1,int pos2)147   int CompareUpToTail(int pos1, int pos2) {
148     if (pos1 < len1_) {
149       if (pos2 < len2_) {
150         int cached_res = get_value4(pos1, pos2);
151         if (cached_res == kEmptyCellValue) {
152           Direction dir;
153           int res;
154           if (input_->Equals(pos1, pos2)) {
155             res = CompareUpToTail(pos1 + 1, pos2 + 1);
156             dir = EQ;
157           } else {
158             int res1 = CompareUpToTail(pos1 + 1, pos2) +
159                 (1 << kDirectionSizeBits);
160             int res2 = CompareUpToTail(pos1, pos2 + 1) +
161                 (1 << kDirectionSizeBits);
162             if (res1 == res2) {
163               res = res1;
164               dir = SKIP_ANY;
165             } else if (res1 < res2) {
166               res = res1;
167               dir = SKIP1;
168             } else {
169               res = res2;
170               dir = SKIP2;
171             }
172           }
173           set_value4_and_dir(pos1, pos2, res, dir);
174           cached_res = res;
175         }
176         return cached_res;
177       } else {
178         return (len1_ - pos1) << kDirectionSizeBits;
179       }
180     } else {
181       return (len2_ - pos2) << kDirectionSizeBits;
182     }
183   }
184 
get_cell(int i1,int i2)185   inline int& get_cell(int i1, int i2) {
186     return buffer_[i1 + i2 * len1_];
187   }
188 
189   // Each cell keeps a value plus direction. Value is multiplied by 4.
set_value4_and_dir(int i1,int i2,int value4,Direction dir)190   void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
191     ASSERT((value4 & kDirectionMask) == 0);
192     get_cell(i1, i2) = value4 | dir;
193   }
194 
get_value4(int i1,int i2)195   int get_value4(int i1, int i2) {
196     return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
197   }
get_direction(int i1,int i2)198   Direction get_direction(int i1, int i2) {
199     return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
200   }
201 
202   static const int kDirectionSizeBits = 2;
203   static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
204   static const int kEmptyCellValue = -1 << kDirectionSizeBits;
205 
206   // This method only holds static assert statement (unfortunately you cannot
207   // place one in class scope).
StaticAssertHolder()208   void StaticAssertHolder() {
209     STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
210   }
211 
212   class ResultWriter {
213    public:
ResultWriter(Comparator::Output * chunk_writer)214     explicit ResultWriter(Comparator::Output* chunk_writer)
215         : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
216           pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
217     }
eq()218     void eq() {
219       FlushChunk();
220       pos1_++;
221       pos2_++;
222     }
skip1(int len1)223     void skip1(int len1) {
224       StartChunk();
225       pos1_ += len1;
226     }
skip2(int len2)227     void skip2(int len2) {
228       StartChunk();
229       pos2_ += len2;
230     }
close()231     void close() {
232       FlushChunk();
233     }
234 
235    private:
236     Comparator::Output* chunk_writer_;
237     int pos1_;
238     int pos2_;
239     int pos1_begin_;
240     int pos2_begin_;
241     bool has_open_chunk_;
242 
StartChunk()243     void StartChunk() {
244       if (!has_open_chunk_) {
245         pos1_begin_ = pos1_;
246         pos2_begin_ = pos2_;
247         has_open_chunk_ = true;
248       }
249     }
250 
FlushChunk()251     void FlushChunk() {
252       if (has_open_chunk_) {
253         chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
254                                 pos1_ - pos1_begin_, pos2_ - pos2_begin_);
255         has_open_chunk_ = false;
256       }
257     }
258   };
259 };
260 
261 
CalculateDifference(Comparator::Input * input,Comparator::Output * result_writer)262 void Comparator::CalculateDifference(Comparator::Input* input,
263                                      Comparator::Output* result_writer) {
264   Differencer differencer(input);
265   differencer.Initialize();
266   differencer.FillTable();
267   differencer.SaveResult(result_writer);
268 }
269 
270 
CompareSubstrings(Handle<String> s1,int pos1,Handle<String> s2,int pos2,int len)271 static bool CompareSubstrings(Handle<String> s1, int pos1,
272                               Handle<String> s2, int pos2, int len) {
273   for (int i = 0; i < len; i++) {
274     if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
275       return false;
276     }
277   }
278   return true;
279 }
280 
281 
282 // Additional to Input interface. Lets switch Input range to subrange.
283 // More elegant way would be to wrap one Input as another Input object
284 // and translate positions there, but that would cost us additional virtual
285 // call per comparison.
286 class SubrangableInput : public Comparator::Input {
287  public:
288   virtual void SetSubrange1(int offset, int len) = 0;
289   virtual void SetSubrange2(int offset, int len) = 0;
290 };
291 
292 
293 class SubrangableOutput : public Comparator::Output {
294  public:
295   virtual void SetSubrange1(int offset, int len) = 0;
296   virtual void SetSubrange2(int offset, int len) = 0;
297 };
298 
299 
min(int a,int b)300 static int min(int a, int b) {
301   return a < b ? a : b;
302 }
303 
304 
305 // Finds common prefix and suffix in input. This parts shouldn't take space in
306 // linear programming table. Enable subranging in input and output.
NarrowDownInput(SubrangableInput * input,SubrangableOutput * output)307 static void NarrowDownInput(SubrangableInput* input,
308     SubrangableOutput* output) {
309   const int len1 = input->GetLength1();
310   const int len2 = input->GetLength2();
311 
312   int common_prefix_len;
313   int common_suffix_len;
314 
315   {
316     common_prefix_len = 0;
317     int prefix_limit = min(len1, len2);
318     while (common_prefix_len < prefix_limit &&
319         input->Equals(common_prefix_len, common_prefix_len)) {
320       common_prefix_len++;
321     }
322 
323     common_suffix_len = 0;
324     int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
325 
326     while (common_suffix_len < suffix_limit &&
327         input->Equals(len1 - common_suffix_len - 1,
328         len2 - common_suffix_len - 1)) {
329       common_suffix_len++;
330     }
331   }
332 
333   if (common_prefix_len > 0 || common_suffix_len > 0) {
334     int new_len1 = len1 - common_suffix_len - common_prefix_len;
335     int new_len2 = len2 - common_suffix_len - common_prefix_len;
336 
337     input->SetSubrange1(common_prefix_len, new_len1);
338     input->SetSubrange2(common_prefix_len, new_len2);
339 
340     output->SetSubrange1(common_prefix_len, new_len1);
341     output->SetSubrange2(common_prefix_len, new_len2);
342   }
343 }
344 
345 
346 // A helper class that writes chunk numbers into JSArray.
347 // Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
348 class CompareOutputArrayWriter {
349  public:
CompareOutputArrayWriter()350   CompareOutputArrayWriter()
351       : array_(FACTORY->NewJSArray(10)), current_size_(0) {}
352 
GetResult()353   Handle<JSArray> GetResult() {
354     return array_;
355   }
356 
WriteChunk(int char_pos1,int char_pos2,int char_len1,int char_len2)357   void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
358     SetElementNonStrict(array_,
359                        current_size_,
360                        Handle<Object>(Smi::FromInt(char_pos1)));
361     SetElementNonStrict(array_,
362                         current_size_ + 1,
363                         Handle<Object>(Smi::FromInt(char_pos1 + char_len1)));
364     SetElementNonStrict(array_,
365                         current_size_ + 2,
366                         Handle<Object>(Smi::FromInt(char_pos2 + char_len2)));
367     current_size_ += 3;
368   }
369 
370  private:
371   Handle<JSArray> array_;
372   int current_size_;
373 };
374 
375 
376 // Represents 2 strings as 2 arrays of tokens.
377 // TODO(LiveEdit): Currently it's actually an array of charactres.
378 //     Make array of tokens instead.
379 class TokensCompareInput : public Comparator::Input {
380  public:
TokensCompareInput(Handle<String> s1,int offset1,int len1,Handle<String> s2,int offset2,int len2)381   TokensCompareInput(Handle<String> s1, int offset1, int len1,
382                        Handle<String> s2, int offset2, int len2)
383       : s1_(s1), offset1_(offset1), len1_(len1),
384         s2_(s2), offset2_(offset2), len2_(len2) {
385   }
GetLength1()386   virtual int GetLength1() {
387     return len1_;
388   }
GetLength2()389   virtual int GetLength2() {
390     return len2_;
391   }
Equals(int index1,int index2)392   bool Equals(int index1, int index2) {
393     return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
394   }
395 
396  private:
397   Handle<String> s1_;
398   int offset1_;
399   int len1_;
400   Handle<String> s2_;
401   int offset2_;
402   int len2_;
403 };
404 
405 
406 // Stores compare result in JSArray. Converts substring positions
407 // to absolute positions.
408 class TokensCompareOutput : public Comparator::Output {
409  public:
TokensCompareOutput(CompareOutputArrayWriter * array_writer,int offset1,int offset2)410   TokensCompareOutput(CompareOutputArrayWriter* array_writer,
411                       int offset1, int offset2)
412         : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
413   }
414 
AddChunk(int pos1,int pos2,int len1,int len2)415   void AddChunk(int pos1, int pos2, int len1, int len2) {
416     array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
417   }
418 
419  private:
420   CompareOutputArrayWriter* array_writer_;
421   int offset1_;
422   int offset2_;
423 };
424 
425 
426 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
427 // never has terminating new line character.
428 class LineEndsWrapper {
429  public:
LineEndsWrapper(Handle<String> string)430   explicit LineEndsWrapper(Handle<String> string)
431       : ends_array_(CalculateLineEnds(string, false)),
432         string_len_(string->length()) {
433   }
length()434   int length() {
435     return ends_array_->length() + 1;
436   }
437   // Returns start for any line including start of the imaginary line after
438   // the last line.
GetLineStart(int index)439   int GetLineStart(int index) {
440     if (index == 0) {
441       return 0;
442     } else {
443       return GetLineEnd(index - 1);
444     }
445   }
GetLineEnd(int index)446   int GetLineEnd(int index) {
447     if (index == ends_array_->length()) {
448       // End of the last line is always an end of the whole string.
449       // If the string ends with a new line character, the last line is an
450       // empty string after this character.
451       return string_len_;
452     } else {
453       return GetPosAfterNewLine(index);
454     }
455   }
456 
457  private:
458   Handle<FixedArray> ends_array_;
459   int string_len_;
460 
GetPosAfterNewLine(int index)461   int GetPosAfterNewLine(int index) {
462     return Smi::cast(ends_array_->get(index))->value() + 1;
463   }
464 };
465 
466 
467 // Represents 2 strings as 2 arrays of lines.
468 class LineArrayCompareInput : public SubrangableInput {
469  public:
LineArrayCompareInput(Handle<String> s1,Handle<String> s2,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2)470   LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
471                         LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
472       : s1_(s1), s2_(s2), line_ends1_(line_ends1),
473         line_ends2_(line_ends2),
474         subrange_offset1_(0), subrange_offset2_(0),
475         subrange_len1_(line_ends1_.length()),
476         subrange_len2_(line_ends2_.length()) {
477   }
GetLength1()478   int GetLength1() {
479     return subrange_len1_;
480   }
GetLength2()481   int GetLength2() {
482     return subrange_len2_;
483   }
Equals(int index1,int index2)484   bool Equals(int index1, int index2) {
485     index1 += subrange_offset1_;
486     index2 += subrange_offset2_;
487 
488     int line_start1 = line_ends1_.GetLineStart(index1);
489     int line_start2 = line_ends2_.GetLineStart(index2);
490     int line_end1 = line_ends1_.GetLineEnd(index1);
491     int line_end2 = line_ends2_.GetLineEnd(index2);
492     int len1 = line_end1 - line_start1;
493     int len2 = line_end2 - line_start2;
494     if (len1 != len2) {
495       return false;
496     }
497     return CompareSubstrings(s1_, line_start1, s2_, line_start2,
498                              len1);
499   }
SetSubrange1(int offset,int len)500   void SetSubrange1(int offset, int len) {
501     subrange_offset1_ = offset;
502     subrange_len1_ = len;
503   }
SetSubrange2(int offset,int len)504   void SetSubrange2(int offset, int len) {
505     subrange_offset2_ = offset;
506     subrange_len2_ = len;
507   }
508 
509  private:
510   Handle<String> s1_;
511   Handle<String> s2_;
512   LineEndsWrapper line_ends1_;
513   LineEndsWrapper line_ends2_;
514   int subrange_offset1_;
515   int subrange_offset2_;
516   int subrange_len1_;
517   int subrange_len2_;
518 };
519 
520 
521 // Stores compare result in JSArray. For each chunk tries to conduct
522 // a fine-grained nested diff token-wise.
523 class TokenizingLineArrayCompareOutput : public SubrangableOutput {
524  public:
TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,LineEndsWrapper line_ends2,Handle<String> s1,Handle<String> s2)525   TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
526                                    LineEndsWrapper line_ends2,
527                                    Handle<String> s1, Handle<String> s2)
528       : line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
529         subrange_offset1_(0), subrange_offset2_(0) {
530   }
531 
AddChunk(int line_pos1,int line_pos2,int line_len1,int line_len2)532   void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
533     line_pos1 += subrange_offset1_;
534     line_pos2 += subrange_offset2_;
535 
536     int char_pos1 = line_ends1_.GetLineStart(line_pos1);
537     int char_pos2 = line_ends2_.GetLineStart(line_pos2);
538     int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
539     int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
540 
541     if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
542       // Chunk is small enough to conduct a nested token-level diff.
543       HandleScope subTaskScope;
544 
545       TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
546                                       s2_, char_pos2, char_len2);
547       TokensCompareOutput tokens_output(&array_writer_, char_pos1,
548                                           char_pos2);
549 
550       Comparator::CalculateDifference(&tokens_input, &tokens_output);
551     } else {
552       array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
553     }
554   }
SetSubrange1(int offset,int len)555   void SetSubrange1(int offset, int len) {
556     subrange_offset1_ = offset;
557   }
SetSubrange2(int offset,int len)558   void SetSubrange2(int offset, int len) {
559     subrange_offset2_ = offset;
560   }
561 
GetResult()562   Handle<JSArray> GetResult() {
563     return array_writer_.GetResult();
564   }
565 
566  private:
567   static const int CHUNK_LEN_LIMIT = 800;
568 
569   CompareOutputArrayWriter array_writer_;
570   LineEndsWrapper line_ends1_;
571   LineEndsWrapper line_ends2_;
572   Handle<String> s1_;
573   Handle<String> s2_;
574   int subrange_offset1_;
575   int subrange_offset2_;
576 };
577 
578 
CompareStrings(Handle<String> s1,Handle<String> s2)579 Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
580                                          Handle<String> s2) {
581   s1 = FlattenGetString(s1);
582   s2 = FlattenGetString(s2);
583 
584   LineEndsWrapper line_ends1(s1);
585   LineEndsWrapper line_ends2(s2);
586 
587   LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
588   TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
589 
590   NarrowDownInput(&input, &output);
591 
592   Comparator::CalculateDifference(&input, &output);
593 
594   return output.GetResult();
595 }
596 
597 
CompileScriptForTracker(Isolate * isolate,Handle<Script> script)598 static void CompileScriptForTracker(Isolate* isolate, Handle<Script> script) {
599   // TODO(635): support extensions.
600   PostponeInterruptsScope postpone(isolate);
601 
602   // Build AST.
603   CompilationInfo info(script);
604   info.MarkAsGlobal();
605   // Parse and don't allow skipping lazy functions.
606   if (ParserApi::Parse(&info, kNoParsingFlags)) {
607     // Compile the code.
608     LiveEditFunctionTracker tracker(info.isolate(), info.function());
609     if (Compiler::MakeCodeForLiveEdit(&info)) {
610       ASSERT(!info.code().is_null());
611       tracker.RecordRootFunctionInfo(info.code());
612     } else {
613       info.isolate()->StackOverflow();
614     }
615   }
616 }
617 
618 
619 // Unwraps JSValue object, returning its field "value"
UnwrapJSValue(Handle<JSValue> jsValue)620 static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
621   return Handle<Object>(jsValue->value());
622 }
623 
624 
625 // Wraps any object into a OpaqueReference, that will hide the object
626 // from JavaScript.
WrapInJSValue(Handle<Object> object)627 static Handle<JSValue> WrapInJSValue(Handle<Object> object) {
628   Handle<JSFunction> constructor =
629       Isolate::Current()->opaque_reference_function();
630   Handle<JSValue> result =
631       Handle<JSValue>::cast(FACTORY->NewJSObject(constructor));
632   result->set_value(*object);
633   return result;
634 }
635 
636 
637 // Simple helper class that creates more or less typed structures over
638 // JSArray object. This is an adhoc method of passing structures from C++
639 // to JavaScript.
640 template<typename S>
641 class JSArrayBasedStruct {
642  public:
Create()643   static S Create() {
644     Handle<JSArray> array = FACTORY->NewJSArray(S::kSize_);
645     return S(array);
646   }
cast(Object * object)647   static S cast(Object* object) {
648     JSArray* array = JSArray::cast(object);
649     Handle<JSArray> array_handle(array);
650     return S(array_handle);
651   }
JSArrayBasedStruct(Handle<JSArray> array)652   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
653   }
GetJSArray()654   Handle<JSArray> GetJSArray() {
655     return array_;
656   }
657 
658  protected:
SetField(int field_position,Handle<Object> value)659   void SetField(int field_position, Handle<Object> value) {
660     SetElementNonStrict(array_, field_position, value);
661   }
SetSmiValueField(int field_position,int value)662   void SetSmiValueField(int field_position, int value) {
663     SetElementNonStrict(array_,
664                         field_position,
665                         Handle<Smi>(Smi::FromInt(value)));
666   }
GetField(int field_position)667   Object* GetField(int field_position) {
668     return array_->GetElementNoExceptionThrown(field_position);
669   }
GetSmiValueField(int field_position)670   int GetSmiValueField(int field_position) {
671     Object* res = GetField(field_position);
672     return Smi::cast(res)->value();
673   }
674 
675  private:
676   Handle<JSArray> array_;
677 };
678 
679 
680 // Represents some function compilation details. This structure will be used
681 // from JavaScript. It contains Code object, which is kept wrapped
682 // into a BlindReference for sanitizing reasons.
683 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
684  public:
FunctionInfoWrapper(Handle<JSArray> array)685   explicit FunctionInfoWrapper(Handle<JSArray> array)
686       : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
687   }
SetInitialProperties(Handle<String> name,int start_position,int end_position,int param_num,int parent_index)688   void SetInitialProperties(Handle<String> name, int start_position,
689                             int end_position, int param_num, int parent_index) {
690     HandleScope scope;
691     this->SetField(kFunctionNameOffset_, name);
692     this->SetSmiValueField(kStartPositionOffset_, start_position);
693     this->SetSmiValueField(kEndPositionOffset_, end_position);
694     this->SetSmiValueField(kParamNumOffset_, param_num);
695     this->SetSmiValueField(kParentIndexOffset_, parent_index);
696   }
SetFunctionCode(Handle<Code> function_code,Handle<Object> code_scope_info)697   void SetFunctionCode(Handle<Code> function_code,
698       Handle<Object> code_scope_info) {
699     Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
700     this->SetField(kCodeOffset_, code_wrapper);
701 
702     Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
703     this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
704   }
SetOuterScopeInfo(Handle<Object> scope_info_array)705   void SetOuterScopeInfo(Handle<Object> scope_info_array) {
706     this->SetField(kOuterScopeInfoOffset_, scope_info_array);
707   }
SetSharedFunctionInfo(Handle<SharedFunctionInfo> info)708   void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
709     Handle<JSValue> info_holder = WrapInJSValue(info);
710     this->SetField(kSharedFunctionInfoOffset_, info_holder);
711   }
GetParentIndex()712   int GetParentIndex() {
713     return this->GetSmiValueField(kParentIndexOffset_);
714   }
GetFunctionCode()715   Handle<Code> GetFunctionCode() {
716     Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>(
717         JSValue::cast(this->GetField(kCodeOffset_))));
718     return Handle<Code>::cast(raw_result);
719   }
GetCodeScopeInfo()720   Handle<Object> GetCodeScopeInfo() {
721     Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>(
722         JSValue::cast(this->GetField(kCodeScopeInfoOffset_))));
723     return raw_result;
724   }
GetStartPosition()725   int GetStartPosition() {
726     return this->GetSmiValueField(kStartPositionOffset_);
727   }
GetEndPosition()728   int GetEndPosition() {
729     return this->GetSmiValueField(kEndPositionOffset_);
730   }
731 
732  private:
733   static const int kFunctionNameOffset_ = 0;
734   static const int kStartPositionOffset_ = 1;
735   static const int kEndPositionOffset_ = 2;
736   static const int kParamNumOffset_ = 3;
737   static const int kCodeOffset_ = 4;
738   static const int kCodeScopeInfoOffset_ = 5;
739   static const int kOuterScopeInfoOffset_ = 6;
740   static const int kParentIndexOffset_ = 7;
741   static const int kSharedFunctionInfoOffset_ = 8;
742   static const int kSize_ = 9;
743 
744   friend class JSArrayBasedStruct<FunctionInfoWrapper>;
745 };
746 
747 
748 // Wraps SharedFunctionInfo along with some of its fields for passing it
749 // back to JavaScript. SharedFunctionInfo object itself is additionally
750 // wrapped into BlindReference for sanitizing reasons.
751 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
752  public:
IsInstance(Handle<JSArray> array)753   static bool IsInstance(Handle<JSArray> array) {
754     return array->length() == Smi::FromInt(kSize_) &&
755         array->GetElementNoExceptionThrown(kSharedInfoOffset_)->IsJSValue();
756   }
757 
SharedInfoWrapper(Handle<JSArray> array)758   explicit SharedInfoWrapper(Handle<JSArray> array)
759       : JSArrayBasedStruct<SharedInfoWrapper>(array) {
760   }
761 
SetProperties(Handle<String> name,int start_position,int end_position,Handle<SharedFunctionInfo> info)762   void SetProperties(Handle<String> name, int start_position, int end_position,
763                      Handle<SharedFunctionInfo> info) {
764     HandleScope scope;
765     this->SetField(kFunctionNameOffset_, name);
766     Handle<JSValue> info_holder = WrapInJSValue(info);
767     this->SetField(kSharedInfoOffset_, info_holder);
768     this->SetSmiValueField(kStartPositionOffset_, start_position);
769     this->SetSmiValueField(kEndPositionOffset_, end_position);
770   }
GetInfo()771   Handle<SharedFunctionInfo> GetInfo() {
772     Object* element = this->GetField(kSharedInfoOffset_);
773     Handle<JSValue> value_wrapper(JSValue::cast(element));
774     Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
775     return Handle<SharedFunctionInfo>::cast(raw_result);
776   }
777 
778  private:
779   static const int kFunctionNameOffset_ = 0;
780   static const int kStartPositionOffset_ = 1;
781   static const int kEndPositionOffset_ = 2;
782   static const int kSharedInfoOffset_ = 3;
783   static const int kSize_ = 4;
784 
785   friend class JSArrayBasedStruct<SharedInfoWrapper>;
786 };
787 
788 
789 class FunctionInfoListener {
790  public:
FunctionInfoListener()791   FunctionInfoListener() {
792     current_parent_index_ = -1;
793     len_ = 0;
794     result_ = FACTORY->NewJSArray(10);
795   }
796 
FunctionStarted(FunctionLiteral * fun)797   void FunctionStarted(FunctionLiteral* fun) {
798     HandleScope scope;
799     FunctionInfoWrapper info = FunctionInfoWrapper::Create();
800     info.SetInitialProperties(fun->name(), fun->start_position(),
801                               fun->end_position(), fun->parameter_count(),
802                               current_parent_index_);
803     current_parent_index_ = len_;
804     SetElementNonStrict(result_, len_, info.GetJSArray());
805     len_++;
806   }
807 
FunctionDone()808   void FunctionDone() {
809     HandleScope scope;
810     FunctionInfoWrapper info =
811         FunctionInfoWrapper::cast(
812             result_->GetElementNoExceptionThrown(current_parent_index_));
813     current_parent_index_ = info.GetParentIndex();
814   }
815 
816   // Saves only function code, because for a script function we
817   // may never create a SharedFunctionInfo object.
FunctionCode(Handle<Code> function_code)818   void FunctionCode(Handle<Code> function_code) {
819     FunctionInfoWrapper info =
820         FunctionInfoWrapper::cast(
821             result_->GetElementNoExceptionThrown(current_parent_index_));
822     info.SetFunctionCode(function_code, Handle<Object>(HEAP->null_value()));
823   }
824 
825   // Saves full information about a function: its code, its scope info
826   // and a SharedFunctionInfo object.
FunctionInfo(Handle<SharedFunctionInfo> shared,Scope * scope)827   void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope) {
828     if (!shared->IsSharedFunctionInfo()) {
829       return;
830     }
831     FunctionInfoWrapper info =
832         FunctionInfoWrapper::cast(
833             result_->GetElementNoExceptionThrown(current_parent_index_));
834     info.SetFunctionCode(Handle<Code>(shared->code()),
835         Handle<Object>(shared->scope_info()));
836     info.SetSharedFunctionInfo(shared);
837 
838     Handle<Object> scope_info_list(SerializeFunctionScope(scope));
839     info.SetOuterScopeInfo(scope_info_list);
840   }
841 
GetResult()842   Handle<JSArray> GetResult() { return result_; }
843 
844  private:
SerializeFunctionScope(Scope * scope)845   Object* SerializeFunctionScope(Scope* scope) {
846     HandleScope handle_scope;
847 
848     Handle<JSArray> scope_info_list = FACTORY->NewJSArray(10);
849     int scope_info_length = 0;
850 
851     // Saves some description of scope. It stores name and indexes of
852     // variables in the whole scope chain. Null-named slots delimit
853     // scopes of this chain.
854     Scope* outer_scope = scope->outer_scope();
855     if (outer_scope == NULL) {
856       return HEAP->undefined_value();
857     }
858     do {
859       ZoneList<Variable*> stack_list(outer_scope->StackLocalCount());
860       ZoneList<Variable*> context_list(outer_scope->ContextLocalCount());
861       outer_scope->CollectStackAndContextLocals(&stack_list, &context_list);
862       context_list.Sort(&Variable::CompareIndex);
863 
864       for (int i = 0; i < context_list.length(); i++) {
865         SetElementNonStrict(scope_info_list,
866                             scope_info_length,
867                             context_list[i]->name());
868         scope_info_length++;
869         SetElementNonStrict(
870             scope_info_list,
871             scope_info_length,
872             Handle<Smi>(Smi::FromInt(context_list[i]->index())));
873         scope_info_length++;
874       }
875       SetElementNonStrict(scope_info_list,
876                           scope_info_length,
877                           Handle<Object>(HEAP->null_value()));
878       scope_info_length++;
879 
880       outer_scope = outer_scope->outer_scope();
881     } while (outer_scope != NULL);
882 
883     return *scope_info_list;
884   }
885 
886   Handle<JSArray> result_;
887   int len_;
888   int current_parent_index_;
889 };
890 
891 
GatherCompileInfo(Handle<Script> script,Handle<String> source)892 JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
893                                      Handle<String> source) {
894   Isolate* isolate = Isolate::Current();
895   ZoneScope zone_scope(isolate, DELETE_ON_EXIT);
896 
897   FunctionInfoListener listener;
898   Handle<Object> original_source = Handle<Object>(script->source());
899   script->set_source(*source);
900   isolate->set_active_function_info_listener(&listener);
901   CompileScriptForTracker(isolate, script);
902   isolate->set_active_function_info_listener(NULL);
903   script->set_source(*original_source);
904 
905   return *(listener.GetResult());
906 }
907 
908 
WrapSharedFunctionInfos(Handle<JSArray> array)909 void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
910   HandleScope scope;
911   int len = Smi::cast(array->length())->value();
912   for (int i = 0; i < len; i++) {
913     Handle<SharedFunctionInfo> info(
914         SharedFunctionInfo::cast(array->GetElementNoExceptionThrown(i)));
915     SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create();
916     Handle<String> name_handle(String::cast(info->name()));
917     info_wrapper.SetProperties(name_handle, info->start_position(),
918                                info->end_position(), info);
919     SetElementNonStrict(array, i, info_wrapper.GetJSArray());
920   }
921 }
922 
923 
924 // Visitor that collects all references to a particular code object,
925 // including "CODE_TARGET" references in other code objects.
926 // It works in context of ZoneScope.
927 class ReferenceCollectorVisitor : public ObjectVisitor {
928  public:
ReferenceCollectorVisitor(Code * original)929   explicit ReferenceCollectorVisitor(Code* original)
930     : original_(original), rvalues_(10), reloc_infos_(10), code_entries_(10) {
931   }
932 
VisitPointers(Object ** start,Object ** end)933   virtual void VisitPointers(Object** start, Object** end) {
934     for (Object** p = start; p < end; p++) {
935       if (*p == original_) {
936         rvalues_.Add(p);
937       }
938     }
939   }
940 
VisitCodeEntry(Address entry)941   virtual void VisitCodeEntry(Address entry) {
942     if (Code::GetObjectFromEntryAddress(entry) == original_) {
943       code_entries_.Add(entry);
944     }
945   }
946 
VisitCodeTarget(RelocInfo * rinfo)947   virtual void VisitCodeTarget(RelocInfo* rinfo) {
948     if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
949         Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
950       reloc_infos_.Add(*rinfo);
951     }
952   }
953 
VisitDebugTarget(RelocInfo * rinfo)954   virtual void VisitDebugTarget(RelocInfo* rinfo) {
955     VisitCodeTarget(rinfo);
956   }
957 
958   // Post-visiting method that iterates over all collected references and
959   // modifies them.
Replace(Code * substitution)960   void Replace(Code* substitution) {
961     for (int i = 0; i < rvalues_.length(); i++) {
962       *(rvalues_[i]) = substitution;
963     }
964     Address substitution_entry = substitution->instruction_start();
965     for (int i = 0; i < reloc_infos_.length(); i++) {
966       reloc_infos_[i].set_target_address(substitution_entry);
967     }
968     for (int i = 0; i < code_entries_.length(); i++) {
969       Address entry = code_entries_[i];
970       Memory::Address_at(entry) = substitution_entry;
971     }
972   }
973 
974  private:
975   Code* original_;
976   ZoneList<Object**> rvalues_;
977   ZoneList<RelocInfo> reloc_infos_;
978   ZoneList<Address> code_entries_;
979 };
980 
981 
982 // Finds all references to original and replaces them with substitution.
ReplaceCodeObject(Code * original,Code * substitution)983 static void ReplaceCodeObject(Code* original, Code* substitution) {
984   ASSERT(!HEAP->InNewSpace(substitution));
985 
986   HeapIterator iterator;
987   AssertNoAllocation no_allocations_please;
988 
989   // A zone scope for ReferenceCollectorVisitor.
990   ZoneScope scope(Isolate::Current(), DELETE_ON_EXIT);
991 
992   ReferenceCollectorVisitor visitor(original);
993 
994   // Iterate over all roots. Stack frames may have pointer into original code,
995   // so temporary replace the pointers with offset numbers
996   // in prologue/epilogue.
997   {
998     HEAP->IterateStrongRoots(&visitor, VISIT_ALL);
999   }
1000 
1001   // Now iterate over all pointers of all objects, including code_target
1002   // implicit pointers.
1003   for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1004     obj->Iterate(&visitor);
1005   }
1006 
1007   visitor.Replace(substitution);
1008 }
1009 
1010 
1011 // Check whether the code is natural function code (not a lazy-compile stub
1012 // code).
IsJSFunctionCode(Code * code)1013 static bool IsJSFunctionCode(Code* code) {
1014   return code->kind() == Code::FUNCTION;
1015 }
1016 
1017 
1018 // Returns true if an instance of candidate were inlined into function's code.
IsInlined(JSFunction * function,SharedFunctionInfo * candidate)1019 static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) {
1020   AssertNoAllocation no_gc;
1021 
1022   if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
1023 
1024   DeoptimizationInputData* data =
1025       DeoptimizationInputData::cast(function->code()->deoptimization_data());
1026 
1027   if (data == HEAP->empty_fixed_array()) return false;
1028 
1029   FixedArray* literals = data->LiteralArray();
1030 
1031   int inlined_count = data->InlinedFunctionCount()->value();
1032   for (int i = 0; i < inlined_count; ++i) {
1033     JSFunction* inlined = JSFunction::cast(literals->get(i));
1034     if (inlined->shared() == candidate) return true;
1035   }
1036 
1037   return false;
1038 }
1039 
1040 
1041 class DependentFunctionsDeoptimizingVisitor : public OptimizedFunctionVisitor {
1042  public:
DependentFunctionsDeoptimizingVisitor(SharedFunctionInfo * function_info)1043   explicit DependentFunctionsDeoptimizingVisitor(
1044       SharedFunctionInfo* function_info)
1045       : function_info_(function_info) {}
1046 
EnterContext(Context * context)1047   virtual void EnterContext(Context* context) {
1048   }
1049 
VisitFunction(JSFunction * function)1050   virtual void VisitFunction(JSFunction* function) {
1051     if (function->shared() == function_info_ ||
1052         IsInlined(function, function_info_)) {
1053       Deoptimizer::DeoptimizeFunction(function);
1054     }
1055   }
1056 
LeaveContext(Context * context)1057   virtual void LeaveContext(Context* context) {
1058   }
1059 
1060  private:
1061   SharedFunctionInfo* function_info_;
1062 };
1063 
1064 
DeoptimizeDependentFunctions(SharedFunctionInfo * function_info)1065 static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
1066   AssertNoAllocation no_allocation;
1067 
1068   DependentFunctionsDeoptimizingVisitor visitor(function_info);
1069   Deoptimizer::VisitAllOptimizedFunctions(&visitor);
1070 }
1071 
1072 
ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,Handle<JSArray> shared_info_array)1073 MaybeObject* LiveEdit::ReplaceFunctionCode(
1074     Handle<JSArray> new_compile_info_array,
1075     Handle<JSArray> shared_info_array) {
1076   HandleScope scope;
1077 
1078   if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
1079     return Isolate::Current()->ThrowIllegalOperation();
1080   }
1081 
1082   FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
1083   SharedInfoWrapper shared_info_wrapper(shared_info_array);
1084 
1085   Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1086 
1087   HEAP->EnsureHeapIsIterable();
1088 
1089   if (IsJSFunctionCode(shared_info->code())) {
1090     Handle<Code> code = compile_info_wrapper.GetFunctionCode();
1091     ReplaceCodeObject(shared_info->code(), *code);
1092     Handle<Object> code_scope_info =  compile_info_wrapper.GetCodeScopeInfo();
1093     if (code_scope_info->IsFixedArray()) {
1094       shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info));
1095     }
1096   }
1097 
1098   if (shared_info->debug_info()->IsDebugInfo()) {
1099     Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
1100     Handle<Code> new_original_code =
1101         FACTORY->CopyCode(compile_info_wrapper.GetFunctionCode());
1102     debug_info->set_original_code(*new_original_code);
1103   }
1104 
1105   int start_position = compile_info_wrapper.GetStartPosition();
1106   int end_position = compile_info_wrapper.GetEndPosition();
1107   shared_info->set_start_position(start_position);
1108   shared_info->set_end_position(end_position);
1109 
1110   shared_info->set_construct_stub(
1111       Isolate::Current()->builtins()->builtin(
1112           Builtins::kJSConstructStubGeneric));
1113 
1114   DeoptimizeDependentFunctions(*shared_info);
1115   Isolate::Current()->compilation_cache()->Remove(shared_info);
1116 
1117   return HEAP->undefined_value();
1118 }
1119 
1120 
FunctionSourceUpdated(Handle<JSArray> shared_info_array)1121 MaybeObject* LiveEdit::FunctionSourceUpdated(
1122     Handle<JSArray> shared_info_array) {
1123   HandleScope scope;
1124 
1125   if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
1126     return Isolate::Current()->ThrowIllegalOperation();
1127   }
1128 
1129   SharedInfoWrapper shared_info_wrapper(shared_info_array);
1130   Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1131 
1132   DeoptimizeDependentFunctions(*shared_info);
1133   Isolate::Current()->compilation_cache()->Remove(shared_info);
1134 
1135   return HEAP->undefined_value();
1136 }
1137 
1138 
SetFunctionScript(Handle<JSValue> function_wrapper,Handle<Object> script_handle)1139 void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
1140                                  Handle<Object> script_handle) {
1141   Handle<SharedFunctionInfo> shared_info =
1142       Handle<SharedFunctionInfo>::cast(UnwrapJSValue(function_wrapper));
1143   shared_info->set_script(*script_handle);
1144 
1145   Isolate::Current()->compilation_cache()->Remove(shared_info);
1146 }
1147 
1148 
1149 // For a script text change (defined as position_change_array), translates
1150 // position in unchanged text to position in changed text.
1151 // Text change is a set of non-overlapping regions in text, that have changed
1152 // their contents and length. It is specified as array of groups of 3 numbers:
1153 // (change_begin, change_end, change_end_new_position).
1154 // Each group describes a change in text; groups are sorted by change_begin.
1155 // Only position in text beyond any changes may be successfully translated.
1156 // If a positions is inside some region that changed, result is currently
1157 // undefined.
TranslatePosition(int original_position,Handle<JSArray> position_change_array)1158 static int TranslatePosition(int original_position,
1159                              Handle<JSArray> position_change_array) {
1160   int position_diff = 0;
1161   int array_len = Smi::cast(position_change_array->length())->value();
1162   // TODO(635): binary search may be used here
1163   for (int i = 0; i < array_len; i += 3) {
1164     Object* element = position_change_array->GetElementNoExceptionThrown(i);
1165     int chunk_start = Smi::cast(element)->value();
1166     if (original_position < chunk_start) {
1167       break;
1168     }
1169     element = position_change_array->GetElementNoExceptionThrown(i + 1);
1170     int chunk_end = Smi::cast(element)->value();
1171     // Position mustn't be inside a chunk.
1172     ASSERT(original_position >= chunk_end);
1173     element = position_change_array->GetElementNoExceptionThrown(i + 2);
1174     int chunk_changed_end = Smi::cast(element)->value();
1175     position_diff = chunk_changed_end - chunk_end;
1176   }
1177 
1178   return original_position + position_diff;
1179 }
1180 
1181 
1182 // Auto-growing buffer for writing relocation info code section. This buffer
1183 // is a simplified version of buffer from Assembler. Unlike Assembler, this
1184 // class is platform-independent and it works without dealing with instructions.
1185 // As specified by RelocInfo format, the buffer is filled in reversed order:
1186 // from upper to lower addresses.
1187 // It uses NewArray/DeleteArray for memory management.
1188 class RelocInfoBuffer {
1189  public:
RelocInfoBuffer(int buffer_initial_capicity,byte * pc)1190   RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
1191     buffer_size_ = buffer_initial_capicity + kBufferGap;
1192     buffer_ = NewArray<byte>(buffer_size_);
1193 
1194     reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
1195   }
~RelocInfoBuffer()1196   ~RelocInfoBuffer() {
1197     DeleteArray(buffer_);
1198   }
1199 
1200   // As specified by RelocInfo format, the buffer is filled in reversed order:
1201   // from upper to lower addresses.
Write(const RelocInfo * rinfo)1202   void Write(const RelocInfo* rinfo) {
1203     if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
1204       Grow();
1205     }
1206     reloc_info_writer_.Write(rinfo);
1207   }
1208 
GetResult()1209   Vector<byte> GetResult() {
1210     // Return the bytes from pos up to end of buffer.
1211     int result_size =
1212         static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
1213     return Vector<byte>(reloc_info_writer_.pos(), result_size);
1214   }
1215 
1216  private:
Grow()1217   void Grow() {
1218     // Compute new buffer size.
1219     int new_buffer_size;
1220     if (buffer_size_ < 2 * KB) {
1221       new_buffer_size = 4 * KB;
1222     } else {
1223       new_buffer_size = 2 * buffer_size_;
1224     }
1225     // Some internal data structures overflow for very large buffers,
1226     // they must ensure that kMaximalBufferSize is not too large.
1227     if (new_buffer_size > kMaximalBufferSize) {
1228       V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
1229     }
1230 
1231     // Set up new buffer.
1232     byte* new_buffer = NewArray<byte>(new_buffer_size);
1233 
1234     // Copy the data.
1235     int curently_used_size =
1236         static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
1237     memmove(new_buffer + new_buffer_size - curently_used_size,
1238             reloc_info_writer_.pos(), curently_used_size);
1239 
1240     reloc_info_writer_.Reposition(
1241         new_buffer + new_buffer_size - curently_used_size,
1242         reloc_info_writer_.last_pc());
1243 
1244     DeleteArray(buffer_);
1245     buffer_ = new_buffer;
1246     buffer_size_ = new_buffer_size;
1247   }
1248 
1249   RelocInfoWriter reloc_info_writer_;
1250   byte* buffer_;
1251   int buffer_size_;
1252 
1253   static const int kBufferGap = RelocInfoWriter::kMaxSize;
1254   static const int kMaximalBufferSize = 512*MB;
1255 };
1256 
1257 // Patch positions in code (changes relocation info section) and possibly
1258 // returns new instance of code.
PatchPositionsInCode(Handle<Code> code,Handle<JSArray> position_change_array)1259 static Handle<Code> PatchPositionsInCode(
1260     Handle<Code> code,
1261     Handle<JSArray> position_change_array) {
1262 
1263   RelocInfoBuffer buffer_writer(code->relocation_size(),
1264                                 code->instruction_start());
1265 
1266   {
1267     AssertNoAllocation no_allocations_please;
1268     for (RelocIterator it(*code); !it.done(); it.next()) {
1269       RelocInfo* rinfo = it.rinfo();
1270       if (RelocInfo::IsPosition(rinfo->rmode())) {
1271         int position = static_cast<int>(rinfo->data());
1272         int new_position = TranslatePosition(position,
1273                                              position_change_array);
1274         if (position != new_position) {
1275           RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL);
1276           buffer_writer.Write(&info_copy);
1277           continue;
1278         }
1279       }
1280       buffer_writer.Write(it.rinfo());
1281     }
1282   }
1283 
1284   Vector<byte> buffer = buffer_writer.GetResult();
1285 
1286   if (buffer.length() == code->relocation_size()) {
1287     // Simply patch relocation area of code.
1288     memcpy(code->relocation_start(), buffer.start(), buffer.length());
1289     return code;
1290   } else {
1291     // Relocation info section now has different size. We cannot simply
1292     // rewrite it inside code object. Instead we have to create a new
1293     // code object.
1294     Handle<Code> result(FACTORY->CopyCode(code, buffer));
1295     return result;
1296   }
1297 }
1298 
1299 
PatchFunctionPositions(Handle<JSArray> shared_info_array,Handle<JSArray> position_change_array)1300 MaybeObject* LiveEdit::PatchFunctionPositions(
1301     Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) {
1302 
1303   if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
1304     return Isolate::Current()->ThrowIllegalOperation();
1305   }
1306 
1307   SharedInfoWrapper shared_info_wrapper(shared_info_array);
1308   Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
1309 
1310   int old_function_start = info->start_position();
1311   int new_function_start = TranslatePosition(old_function_start,
1312                                              position_change_array);
1313   int new_function_end = TranslatePosition(info->end_position(),
1314                                            position_change_array);
1315   int new_function_token_pos =
1316       TranslatePosition(info->function_token_position(), position_change_array);
1317 
1318   info->set_start_position(new_function_start);
1319   info->set_end_position(new_function_end);
1320   info->set_function_token_position(new_function_token_pos);
1321 
1322   HEAP->EnsureHeapIsIterable();
1323 
1324   if (IsJSFunctionCode(info->code())) {
1325     // Patch relocation info section of the code.
1326     Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
1327                                                      position_change_array);
1328     if (*patched_code != info->code()) {
1329       // Replace all references to the code across the heap. In particular,
1330       // some stubs may refer to this code and this code may be being executed
1331       // on stack (it is safe to substitute the code object on stack, because
1332       // we only change the structure of rinfo and leave instructions
1333       // untouched).
1334       ReplaceCodeObject(info->code(), *patched_code);
1335     }
1336   }
1337 
1338   return HEAP->undefined_value();
1339 }
1340 
1341 
CreateScriptCopy(Handle<Script> original)1342 static Handle<Script> CreateScriptCopy(Handle<Script> original) {
1343   Handle<String> original_source(String::cast(original->source()));
1344 
1345   Handle<Script> copy = FACTORY->NewScript(original_source);
1346 
1347   copy->set_name(original->name());
1348   copy->set_line_offset(original->line_offset());
1349   copy->set_column_offset(original->column_offset());
1350   copy->set_data(original->data());
1351   copy->set_type(original->type());
1352   copy->set_context_data(original->context_data());
1353   copy->set_compilation_type(original->compilation_type());
1354   copy->set_eval_from_shared(original->eval_from_shared());
1355   copy->set_eval_from_instructions_offset(
1356       original->eval_from_instructions_offset());
1357 
1358   return copy;
1359 }
1360 
1361 
ChangeScriptSource(Handle<Script> original_script,Handle<String> new_source,Handle<Object> old_script_name)1362 Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script,
1363                                      Handle<String> new_source,
1364                                      Handle<Object> old_script_name) {
1365   Handle<Object> old_script_object;
1366   if (old_script_name->IsString()) {
1367     Handle<Script> old_script = CreateScriptCopy(original_script);
1368     old_script->set_name(String::cast(*old_script_name));
1369     old_script_object = old_script;
1370     Isolate::Current()->debugger()->OnAfterCompile(
1371         old_script, Debugger::SEND_WHEN_DEBUGGING);
1372   } else {
1373     old_script_object = Handle<Object>(HEAP->null_value());
1374   }
1375 
1376   original_script->set_source(*new_source);
1377 
1378   // Drop line ends so that they will be recalculated.
1379   original_script->set_line_ends(HEAP->undefined_value());
1380 
1381   return *old_script_object;
1382 }
1383 
1384 
1385 
ReplaceRefToNestedFunction(Handle<JSValue> parent_function_wrapper,Handle<JSValue> orig_function_wrapper,Handle<JSValue> subst_function_wrapper)1386 void LiveEdit::ReplaceRefToNestedFunction(
1387     Handle<JSValue> parent_function_wrapper,
1388     Handle<JSValue> orig_function_wrapper,
1389     Handle<JSValue> subst_function_wrapper) {
1390 
1391   Handle<SharedFunctionInfo> parent_shared =
1392       Handle<SharedFunctionInfo>::cast(UnwrapJSValue(parent_function_wrapper));
1393   Handle<SharedFunctionInfo> orig_shared =
1394       Handle<SharedFunctionInfo>::cast(UnwrapJSValue(orig_function_wrapper));
1395   Handle<SharedFunctionInfo> subst_shared =
1396       Handle<SharedFunctionInfo>::cast(UnwrapJSValue(subst_function_wrapper));
1397 
1398   for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
1399     if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
1400       if (it.rinfo()->target_object() == *orig_shared) {
1401         it.rinfo()->set_target_object(*subst_shared);
1402       }
1403     }
1404   }
1405 }
1406 
1407 
1408 // Check an activation against list of functions. If there is a function
1409 // 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)1410 static bool CheckActivation(Handle<JSArray> shared_info_array,
1411                             Handle<JSArray> result,
1412                             StackFrame* frame,
1413                             LiveEdit::FunctionPatchabilityStatus status) {
1414   if (!frame->is_java_script()) return false;
1415 
1416   Handle<JSFunction> function(
1417       JSFunction::cast(JavaScriptFrame::cast(frame)->function()));
1418 
1419   int len = Smi::cast(shared_info_array->length())->value();
1420   for (int i = 0; i < len; i++) {
1421     JSValue* wrapper =
1422         JSValue::cast(shared_info_array->GetElementNoExceptionThrown(i));
1423     Handle<SharedFunctionInfo> shared(
1424         SharedFunctionInfo::cast(wrapper->value()));
1425 
1426     if (function->shared() == *shared || IsInlined(*function, *shared)) {
1427       SetElementNonStrict(result, i, Handle<Smi>(Smi::FromInt(status)));
1428       return true;
1429     }
1430   }
1431   return false;
1432 }
1433 
1434 
1435 // Iterates over handler chain and removes all elements that are inside
1436 // frames being dropped.
FixTryCatchHandler(StackFrame * top_frame,StackFrame * bottom_frame)1437 static bool FixTryCatchHandler(StackFrame* top_frame,
1438                                StackFrame* bottom_frame) {
1439   Address* pointer_address =
1440       &Memory::Address_at(Isolate::Current()->get_address_from_id(
1441           Isolate::kHandlerAddress));
1442 
1443   while (*pointer_address < top_frame->sp()) {
1444     pointer_address = &Memory::Address_at(*pointer_address);
1445   }
1446   Address* above_frame_address = pointer_address;
1447   while (*pointer_address < bottom_frame->fp()) {
1448     pointer_address = &Memory::Address_at(*pointer_address);
1449   }
1450   bool change = *above_frame_address != *pointer_address;
1451   *above_frame_address = *pointer_address;
1452   return change;
1453 }
1454 
1455 
1456 // Removes specified range of frames from stack. There may be 1 or more
1457 // frames in range. Anyway the bottom frame is restarted rather than dropped,
1458 // and therefore has to be a JavaScript frame.
1459 // Returns error message or NULL.
DropFrames(Vector<StackFrame * > frames,int top_frame_index,int bottom_js_frame_index,Debug::FrameDropMode * mode,Object *** restarter_frame_function_pointer)1460 static const char* DropFrames(Vector<StackFrame*> frames,
1461                               int top_frame_index,
1462                               int bottom_js_frame_index,
1463                               Debug::FrameDropMode* mode,
1464                               Object*** restarter_frame_function_pointer) {
1465   if (!Debug::kFrameDropperSupported) {
1466     return "Stack manipulations are not supported in this architecture.";
1467   }
1468 
1469   StackFrame* pre_top_frame = frames[top_frame_index - 1];
1470   StackFrame* top_frame = frames[top_frame_index];
1471   StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
1472 
1473   ASSERT(bottom_js_frame->is_java_script());
1474 
1475   // Check the nature of the top frame.
1476   Isolate* isolate = Isolate::Current();
1477   Code* pre_top_frame_code = pre_top_frame->LookupCode();
1478   if (pre_top_frame_code->is_inline_cache_stub() &&
1479       pre_top_frame_code->ic_state() == DEBUG_BREAK) {
1480     // OK, we can drop inline cache calls.
1481     *mode = Debug::FRAME_DROPPED_IN_IC_CALL;
1482   } else if (pre_top_frame_code ==
1483              isolate->debug()->debug_break_slot()) {
1484     // OK, we can drop debug break slot.
1485     *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
1486   } else if (pre_top_frame_code ==
1487       isolate->builtins()->builtin(
1488           Builtins::kFrameDropper_LiveEdit)) {
1489     // OK, we can drop our own code.
1490     *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
1491   } else if (pre_top_frame_code ==
1492       isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
1493     *mode = Debug::FRAME_DROPPED_IN_RETURN_CALL;
1494   } else if (pre_top_frame_code->kind() == Code::STUB &&
1495       pre_top_frame_code->major_key()) {
1496     // Entry from our unit tests, it's fine, we support this case.
1497     *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
1498   } else {
1499     return "Unknown structure of stack above changing function";
1500   }
1501 
1502   Address unused_stack_top = top_frame->sp();
1503   Address unused_stack_bottom = bottom_js_frame->fp()
1504       - Debug::kFrameDropperFrameSize * kPointerSize  // Size of the new frame.
1505       + kPointerSize;  // Bigger address end is exclusive.
1506 
1507   if (unused_stack_top > unused_stack_bottom) {
1508     return "Not enough space for frame dropper frame";
1509   }
1510 
1511   // Committing now. After this point we should return only NULL value.
1512 
1513   FixTryCatchHandler(pre_top_frame, bottom_js_frame);
1514   // Make sure FixTryCatchHandler is idempotent.
1515   ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
1516 
1517   Handle<Code> code = Isolate::Current()->builtins()->FrameDropper_LiveEdit();
1518   top_frame->set_pc(code->entry());
1519   pre_top_frame->SetCallerFp(bottom_js_frame->fp());
1520 
1521   *restarter_frame_function_pointer =
1522       Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
1523 
1524   ASSERT((**restarter_frame_function_pointer)->IsJSFunction());
1525 
1526   for (Address a = unused_stack_top;
1527       a < unused_stack_bottom;
1528       a += kPointerSize) {
1529     Memory::Object_at(a) = Smi::FromInt(0);
1530   }
1531 
1532   return NULL;
1533 }
1534 
1535 
IsDropableFrame(StackFrame * frame)1536 static bool IsDropableFrame(StackFrame* frame) {
1537   return !frame->is_exit();
1538 }
1539 
1540 // Fills result array with statuses of functions. Modifies the stack
1541 // removing all listed function if possible and if do_drop is true.
DropActivationsInActiveThread(Handle<JSArray> shared_info_array,Handle<JSArray> result,bool do_drop)1542 static const char* DropActivationsInActiveThread(
1543     Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
1544   Isolate* isolate = Isolate::Current();
1545   Debug* debug = isolate->debug();
1546   ZoneScope scope(isolate, DELETE_ON_EXIT);
1547   Vector<StackFrame*> frames = CreateStackMap();
1548 
1549   int array_len = Smi::cast(shared_info_array->length())->value();
1550 
1551   int top_frame_index = -1;
1552   int frame_index = 0;
1553   for (; frame_index < frames.length(); frame_index++) {
1554     StackFrame* frame = frames[frame_index];
1555     if (frame->id() == debug->break_frame_id()) {
1556       top_frame_index = frame_index;
1557       break;
1558     }
1559     if (CheckActivation(shared_info_array, result, frame,
1560                         LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
1561       // We are still above break_frame. It is not a target frame,
1562       // it is a problem.
1563       return "Debugger mark-up on stack is not found";
1564     }
1565   }
1566 
1567   if (top_frame_index == -1) {
1568     // We haven't found break frame, but no function is blocking us anyway.
1569     return NULL;
1570   }
1571 
1572   bool target_frame_found = false;
1573   int bottom_js_frame_index = top_frame_index;
1574   bool c_code_found = false;
1575 
1576   for (; frame_index < frames.length(); frame_index++) {
1577     StackFrame* frame = frames[frame_index];
1578     if (!IsDropableFrame(frame)) {
1579       c_code_found = true;
1580       break;
1581     }
1582     if (CheckActivation(shared_info_array, result, frame,
1583                         LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
1584       target_frame_found = true;
1585       bottom_js_frame_index = frame_index;
1586     }
1587   }
1588 
1589   if (c_code_found) {
1590     // There is a C frames on stack. Check that there are no target frames
1591     // below them.
1592     for (; frame_index < frames.length(); frame_index++) {
1593       StackFrame* frame = frames[frame_index];
1594       if (frame->is_java_script()) {
1595         if (CheckActivation(shared_info_array, result, frame,
1596                             LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
1597           // Cannot drop frame under C frames.
1598           return NULL;
1599         }
1600       }
1601     }
1602   }
1603 
1604   if (!do_drop) {
1605     // We are in check-only mode.
1606     return NULL;
1607   }
1608 
1609   if (!target_frame_found) {
1610     // Nothing to drop.
1611     return NULL;
1612   }
1613 
1614   Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED;
1615   Object** restarter_frame_function_pointer = NULL;
1616   const char* error_message = DropFrames(frames, top_frame_index,
1617                                          bottom_js_frame_index, &drop_mode,
1618                                          &restarter_frame_function_pointer);
1619 
1620   if (error_message != NULL) {
1621     return error_message;
1622   }
1623 
1624   // Adjust break_frame after some frames has been dropped.
1625   StackFrame::Id new_id = StackFrame::NO_ID;
1626   for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
1627     if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
1628       new_id = frames[i]->id();
1629       break;
1630     }
1631   }
1632   debug->FramesHaveBeenDropped(new_id, drop_mode,
1633                                restarter_frame_function_pointer);
1634 
1635   // Replace "blocked on active" with "replaced on active" status.
1636   for (int i = 0; i < array_len; i++) {
1637     if (result->GetElement(i) ==
1638         Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
1639       Handle<Object> replaced(
1640           Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK));
1641       SetElementNonStrict(result, i, replaced);
1642     }
1643   }
1644   return NULL;
1645 }
1646 
1647 
1648 class InactiveThreadActivationsChecker : public ThreadVisitor {
1649  public:
InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,Handle<JSArray> result)1650   InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
1651                                    Handle<JSArray> result)
1652       : shared_info_array_(shared_info_array), result_(result),
1653         has_blocked_functions_(false) {
1654   }
VisitThread(Isolate * isolate,ThreadLocalTop * top)1655   void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
1656     for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
1657       has_blocked_functions_ |= CheckActivation(
1658           shared_info_array_, result_, it.frame(),
1659           LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
1660     }
1661   }
HasBlockedFunctions()1662   bool HasBlockedFunctions() {
1663     return has_blocked_functions_;
1664   }
1665 
1666  private:
1667   Handle<JSArray> shared_info_array_;
1668   Handle<JSArray> result_;
1669   bool has_blocked_functions_;
1670 };
1671 
1672 
CheckAndDropActivations(Handle<JSArray> shared_info_array,bool do_drop)1673 Handle<JSArray> LiveEdit::CheckAndDropActivations(
1674     Handle<JSArray> shared_info_array, bool do_drop) {
1675   int len = Smi::cast(shared_info_array->length())->value();
1676 
1677   Handle<JSArray> result = FACTORY->NewJSArray(len);
1678 
1679   // Fill the default values.
1680   for (int i = 0; i < len; i++) {
1681     SetElementNonStrict(
1682         result,
1683         i,
1684         Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH)));
1685   }
1686 
1687 
1688   // First check inactive threads. Fail if some functions are blocked there.
1689   InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
1690                                                             result);
1691   Isolate::Current()->thread_manager()->IterateArchivedThreads(
1692       &inactive_threads_checker);
1693   if (inactive_threads_checker.HasBlockedFunctions()) {
1694     return result;
1695   }
1696 
1697   // Try to drop activations from the current stack.
1698   const char* error_message =
1699       DropActivationsInActiveThread(shared_info_array, result, do_drop);
1700   if (error_message != NULL) {
1701     // Add error message as an array extra element.
1702     Vector<const char> vector_message(error_message, StrLength(error_message));
1703     Handle<String> str = FACTORY->NewStringFromAscii(vector_message);
1704     SetElementNonStrict(result, len, str);
1705   }
1706   return result;
1707 }
1708 
1709 
LiveEditFunctionTracker(Isolate * isolate,FunctionLiteral * fun)1710 LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
1711                                                  FunctionLiteral* fun)
1712     : isolate_(isolate) {
1713   if (isolate_->active_function_info_listener() != NULL) {
1714     isolate_->active_function_info_listener()->FunctionStarted(fun);
1715   }
1716 }
1717 
1718 
~LiveEditFunctionTracker()1719 LiveEditFunctionTracker::~LiveEditFunctionTracker() {
1720   if (isolate_->active_function_info_listener() != NULL) {
1721     isolate_->active_function_info_listener()->FunctionDone();
1722   }
1723 }
1724 
1725 
RecordFunctionInfo(Handle<SharedFunctionInfo> info,FunctionLiteral * lit)1726 void LiveEditFunctionTracker::RecordFunctionInfo(
1727     Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
1728   if (isolate_->active_function_info_listener() != NULL) {
1729     isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope());
1730   }
1731 }
1732 
1733 
RecordRootFunctionInfo(Handle<Code> code)1734 void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
1735   isolate_->active_function_info_listener()->FunctionCode(code);
1736 }
1737 
1738 
IsActive(Isolate * isolate)1739 bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
1740   return isolate->active_function_info_listener() != NULL;
1741 }
1742 
1743 
1744 #else  // ENABLE_DEBUGGER_SUPPORT
1745 
1746 // This ifdef-else-endif section provides working or stub implementation of
1747 // LiveEditFunctionTracker.
1748 LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
1749                                                  FunctionLiteral* fun) {
1750 }
1751 
1752 
1753 LiveEditFunctionTracker::~LiveEditFunctionTracker() {
1754 }
1755 
1756 
1757 void LiveEditFunctionTracker::RecordFunctionInfo(
1758     Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
1759 }
1760 
1761 
1762 void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
1763 }
1764 
1765 
1766 bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
1767   return false;
1768 }
1769 
1770 #endif  // ENABLE_DEBUGGER_SUPPORT
1771 
1772 
1773 
1774 } }  // namespace v8::internal
1775