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