1 // Copyright 2010 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 "compiler.h"
34 #include "compilation-cache.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 no_failure = SetElement(object, index, value, 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(Isolate * isolate,Handle<String> s1,int pos1,Handle<String> s2,int pos2,int len)271 static bool CompareSubstrings(Isolate* isolate, Handle<String> s1, int pos1,
272 Handle<String> s2, int pos2, int len) {
273 StringInputBuffer& buf1 = *isolate->liveedit_compare_substrings_buf1();
274 StringInputBuffer& buf2 = *isolate->liveedit_compare_substrings_buf2();
275 buf1.Reset(*s1);
276 buf1.Seek(pos1);
277 buf2.Reset(*s2);
278 buf2.Seek(pos2);
279 for (int i = 0; i < len; i++) {
280 ASSERT(buf1.has_more() && buf2.has_more());
281 if (buf1.GetNext() != buf2.GetNext()) {
282 return false;
283 }
284 }
285 return true;
286 }
287
288
289 // A helper class that writes chunk numbers into JSArray.
290 // Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
291 class CompareOutputArrayWriter {
292 public:
CompareOutputArrayWriter()293 CompareOutputArrayWriter()
294 : array_(FACTORY->NewJSArray(10)), current_size_(0) {}
295
GetResult()296 Handle<JSArray> GetResult() {
297 return array_;
298 }
299
WriteChunk(int char_pos1,int char_pos2,int char_len1,int char_len2)300 void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
301 SetElementNonStrict(array_,
302 current_size_,
303 Handle<Object>(Smi::FromInt(char_pos1)));
304 SetElementNonStrict(array_,
305 current_size_ + 1,
306 Handle<Object>(Smi::FromInt(char_pos1 + char_len1)));
307 SetElementNonStrict(array_,
308 current_size_ + 2,
309 Handle<Object>(Smi::FromInt(char_pos2 + char_len2)));
310 current_size_ += 3;
311 }
312
313 private:
314 Handle<JSArray> array_;
315 int current_size_;
316 };
317
318
319 // Represents 2 strings as 2 arrays of tokens.
320 // TODO(LiveEdit): Currently it's actually an array of charactres.
321 // Make array of tokens instead.
322 class TokensCompareInput : public Comparator::Input {
323 public:
TokensCompareInput(Handle<String> s1,int offset1,int len1,Handle<String> s2,int offset2,int len2)324 TokensCompareInput(Handle<String> s1, int offset1, int len1,
325 Handle<String> s2, int offset2, int len2)
326 : s1_(s1), offset1_(offset1), len1_(len1),
327 s2_(s2), offset2_(offset2), len2_(len2) {
328 }
getLength1()329 virtual int getLength1() {
330 return len1_;
331 }
getLength2()332 virtual int getLength2() {
333 return len2_;
334 }
equals(int index1,int index2)335 bool equals(int index1, int index2) {
336 return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
337 }
338
339 private:
340 Handle<String> s1_;
341 int offset1_;
342 int len1_;
343 Handle<String> s2_;
344 int offset2_;
345 int len2_;
346 };
347
348
349 // Stores compare result in JSArray. Converts substring positions
350 // to absolute positions.
351 class TokensCompareOutput : public Comparator::Output {
352 public:
TokensCompareOutput(CompareOutputArrayWriter * array_writer,int offset1,int offset2)353 TokensCompareOutput(CompareOutputArrayWriter* array_writer,
354 int offset1, int offset2)
355 : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
356 }
357
AddChunk(int pos1,int pos2,int len1,int len2)358 void AddChunk(int pos1, int pos2, int len1, int len2) {
359 array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
360 }
361
362 private:
363 CompareOutputArrayWriter* array_writer_;
364 int offset1_;
365 int offset2_;
366 };
367
368
369 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
370 // never has terminating new line character.
371 class LineEndsWrapper {
372 public:
LineEndsWrapper(Handle<String> string)373 explicit LineEndsWrapper(Handle<String> string)
374 : ends_array_(CalculateLineEnds(string, false)),
375 string_len_(string->length()) {
376 }
length()377 int length() {
378 return ends_array_->length() + 1;
379 }
380 // Returns start for any line including start of the imaginary line after
381 // the last line.
GetLineStart(int index)382 int GetLineStart(int index) {
383 if (index == 0) {
384 return 0;
385 } else {
386 return GetLineEnd(index - 1);
387 }
388 }
GetLineEnd(int index)389 int GetLineEnd(int index) {
390 if (index == ends_array_->length()) {
391 // End of the last line is always an end of the whole string.
392 // If the string ends with a new line character, the last line is an
393 // empty string after this character.
394 return string_len_;
395 } else {
396 return GetPosAfterNewLine(index);
397 }
398 }
399
400 private:
401 Handle<FixedArray> ends_array_;
402 int string_len_;
403
GetPosAfterNewLine(int index)404 int GetPosAfterNewLine(int index) {
405 return Smi::cast(ends_array_->get(index))->value() + 1;
406 }
407 };
408
409
410 // Represents 2 strings as 2 arrays of lines.
411 class LineArrayCompareInput : public Comparator::Input {
412 public:
LineArrayCompareInput(Isolate * isolate,Handle<String> s1,Handle<String> s2,LineEndsWrapper line_ends1,LineEndsWrapper line_ends2)413 LineArrayCompareInput(Isolate* isolate, Handle<String> s1, Handle<String> s2,
414 LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
415 : isolate_(isolate), s1_(s1), s2_(s2), line_ends1_(line_ends1),
416 line_ends2_(line_ends2) {
417 }
getLength1()418 int getLength1() {
419 return line_ends1_.length();
420 }
getLength2()421 int getLength2() {
422 return line_ends2_.length();
423 }
equals(int index1,int index2)424 bool equals(int index1, int index2) {
425 int line_start1 = line_ends1_.GetLineStart(index1);
426 int line_start2 = line_ends2_.GetLineStart(index2);
427 int line_end1 = line_ends1_.GetLineEnd(index1);
428 int line_end2 = line_ends2_.GetLineEnd(index2);
429 int len1 = line_end1 - line_start1;
430 int len2 = line_end2 - line_start2;
431 if (len1 != len2) {
432 return false;
433 }
434 return CompareSubstrings(isolate_, s1_, line_start1, s2_, line_start2,
435 len1);
436 }
437
438 private:
439 Isolate* isolate_;
440 Handle<String> s1_;
441 Handle<String> s2_;
442 LineEndsWrapper line_ends1_;
443 LineEndsWrapper line_ends2_;
444 };
445
446
447 // Stores compare result in JSArray. For each chunk tries to conduct
448 // a fine-grained nested diff token-wise.
449 class TokenizingLineArrayCompareOutput : public Comparator::Output {
450 public:
TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,LineEndsWrapper line_ends2,Handle<String> s1,Handle<String> s2)451 TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
452 LineEndsWrapper line_ends2,
453 Handle<String> s1, Handle<String> s2)
454 : line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2) {
455 }
456
AddChunk(int line_pos1,int line_pos2,int line_len1,int line_len2)457 void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
458 int char_pos1 = line_ends1_.GetLineStart(line_pos1);
459 int char_pos2 = line_ends2_.GetLineStart(line_pos2);
460 int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
461 int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
462
463 if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
464 // Chunk is small enough to conduct a nested token-level diff.
465 HandleScope subTaskScope;
466
467 TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
468 s2_, char_pos2, char_len2);
469 TokensCompareOutput tokens_output(&array_writer_, char_pos1,
470 char_pos2);
471
472 Comparator::CalculateDifference(&tokens_input, &tokens_output);
473 } else {
474 array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
475 }
476 }
477
GetResult()478 Handle<JSArray> GetResult() {
479 return array_writer_.GetResult();
480 }
481
482 private:
483 static const int CHUNK_LEN_LIMIT = 800;
484
485 CompareOutputArrayWriter array_writer_;
486 LineEndsWrapper line_ends1_;
487 LineEndsWrapper line_ends2_;
488 Handle<String> s1_;
489 Handle<String> s2_;
490 };
491
492
CompareStrings(Handle<String> s1,Handle<String> s2)493 Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
494 Handle<String> s2) {
495 LineEndsWrapper line_ends1(s1);
496 LineEndsWrapper line_ends2(s2);
497
498 LineArrayCompareInput
499 input(Isolate::Current(), s1, s2, line_ends1, line_ends2);
500 TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
501
502 Comparator::CalculateDifference(&input, &output);
503
504 return output.GetResult();
505 }
506
507
CompileScriptForTracker(Isolate * isolate,Handle<Script> script)508 static void CompileScriptForTracker(Isolate* isolate, Handle<Script> script) {
509 // TODO(635): support extensions.
510 PostponeInterruptsScope postpone(isolate);
511
512 // Build AST.
513 CompilationInfo info(script);
514 info.MarkAsGlobal();
515 if (ParserApi::Parse(&info)) {
516 // Compile the code.
517 LiveEditFunctionTracker tracker(info.isolate(), info.function());
518 if (Compiler::MakeCodeForLiveEdit(&info)) {
519 ASSERT(!info.code().is_null());
520 tracker.RecordRootFunctionInfo(info.code());
521 } else {
522 info.isolate()->StackOverflow();
523 }
524 }
525 }
526
527
528 // Unwraps JSValue object, returning its field "value"
UnwrapJSValue(Handle<JSValue> jsValue)529 static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
530 return Handle<Object>(jsValue->value());
531 }
532
533
534 // Wraps any object into a OpaqueReference, that will hide the object
535 // from JavaScript.
WrapInJSValue(Object * object)536 static Handle<JSValue> WrapInJSValue(Object* object) {
537 Handle<JSFunction> constructor =
538 Isolate::Current()->opaque_reference_function();
539 Handle<JSValue> result =
540 Handle<JSValue>::cast(FACTORY->NewJSObject(constructor));
541 result->set_value(object);
542 return result;
543 }
544
545
546 // Simple helper class that creates more or less typed structures over
547 // JSArray object. This is an adhoc method of passing structures from C++
548 // to JavaScript.
549 template<typename S>
550 class JSArrayBasedStruct {
551 public:
Create()552 static S Create() {
553 Handle<JSArray> array = FACTORY->NewJSArray(S::kSize_);
554 return S(array);
555 }
cast(Object * object)556 static S cast(Object* object) {
557 JSArray* array = JSArray::cast(object);
558 Handle<JSArray> array_handle(array);
559 return S(array_handle);
560 }
JSArrayBasedStruct(Handle<JSArray> array)561 explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
562 }
GetJSArray()563 Handle<JSArray> GetJSArray() {
564 return array_;
565 }
566
567 protected:
SetField(int field_position,Handle<Object> value)568 void SetField(int field_position, Handle<Object> value) {
569 SetElementNonStrict(array_, field_position, value);
570 }
SetSmiValueField(int field_position,int value)571 void SetSmiValueField(int field_position, int value) {
572 SetElementNonStrict(array_,
573 field_position,
574 Handle<Smi>(Smi::FromInt(value)));
575 }
GetField(int field_position)576 Object* GetField(int field_position) {
577 return array_->GetElementNoExceptionThrown(field_position);
578 }
GetSmiValueField(int field_position)579 int GetSmiValueField(int field_position) {
580 Object* res = GetField(field_position);
581 return Smi::cast(res)->value();
582 }
583
584 private:
585 Handle<JSArray> array_;
586 };
587
588
589 // Represents some function compilation details. This structure will be used
590 // from JavaScript. It contains Code object, which is kept wrapped
591 // into a BlindReference for sanitizing reasons.
592 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
593 public:
FunctionInfoWrapper(Handle<JSArray> array)594 explicit FunctionInfoWrapper(Handle<JSArray> array)
595 : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
596 }
SetInitialProperties(Handle<String> name,int start_position,int end_position,int param_num,int parent_index)597 void SetInitialProperties(Handle<String> name, int start_position,
598 int end_position, int param_num, int parent_index) {
599 HandleScope scope;
600 this->SetField(kFunctionNameOffset_, name);
601 this->SetSmiValueField(kStartPositionOffset_, start_position);
602 this->SetSmiValueField(kEndPositionOffset_, end_position);
603 this->SetSmiValueField(kParamNumOffset_, param_num);
604 this->SetSmiValueField(kParentIndexOffset_, parent_index);
605 }
SetFunctionCode(Handle<Code> function_code,Handle<Object> code_scope_info)606 void SetFunctionCode(Handle<Code> function_code,
607 Handle<Object> code_scope_info) {
608 Handle<JSValue> code_wrapper = WrapInJSValue(*function_code);
609 this->SetField(kCodeOffset_, code_wrapper);
610
611 Handle<JSValue> scope_wrapper = WrapInJSValue(*code_scope_info);
612 this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
613 }
SetOuterScopeInfo(Handle<Object> scope_info_array)614 void SetOuterScopeInfo(Handle<Object> scope_info_array) {
615 this->SetField(kOuterScopeInfoOffset_, scope_info_array);
616 }
SetSharedFunctionInfo(Handle<SharedFunctionInfo> info)617 void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
618 Handle<JSValue> info_holder = WrapInJSValue(*info);
619 this->SetField(kSharedFunctionInfoOffset_, info_holder);
620 }
GetParentIndex()621 int GetParentIndex() {
622 return this->GetSmiValueField(kParentIndexOffset_);
623 }
GetFunctionCode()624 Handle<Code> GetFunctionCode() {
625 Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>(
626 JSValue::cast(this->GetField(kCodeOffset_))));
627 return Handle<Code>::cast(raw_result);
628 }
GetCodeScopeInfo()629 Handle<Object> GetCodeScopeInfo() {
630 Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>(
631 JSValue::cast(this->GetField(kCodeScopeInfoOffset_))));
632 return raw_result;
633 }
GetStartPosition()634 int GetStartPosition() {
635 return this->GetSmiValueField(kStartPositionOffset_);
636 }
GetEndPosition()637 int GetEndPosition() {
638 return this->GetSmiValueField(kEndPositionOffset_);
639 }
640
641 private:
642 static const int kFunctionNameOffset_ = 0;
643 static const int kStartPositionOffset_ = 1;
644 static const int kEndPositionOffset_ = 2;
645 static const int kParamNumOffset_ = 3;
646 static const int kCodeOffset_ = 4;
647 static const int kCodeScopeInfoOffset_ = 5;
648 static const int kOuterScopeInfoOffset_ = 6;
649 static const int kParentIndexOffset_ = 7;
650 static const int kSharedFunctionInfoOffset_ = 8;
651 static const int kSize_ = 9;
652
653 friend class JSArrayBasedStruct<FunctionInfoWrapper>;
654 };
655
656
657 // Wraps SharedFunctionInfo along with some of its fields for passing it
658 // back to JavaScript. SharedFunctionInfo object itself is additionally
659 // wrapped into BlindReference for sanitizing reasons.
660 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
661 public:
IsInstance(Handle<JSArray> array)662 static bool IsInstance(Handle<JSArray> array) {
663 return array->length() == Smi::FromInt(kSize_) &&
664 array->GetElementNoExceptionThrown(kSharedInfoOffset_)->IsJSValue();
665 }
666
SharedInfoWrapper(Handle<JSArray> array)667 explicit SharedInfoWrapper(Handle<JSArray> array)
668 : JSArrayBasedStruct<SharedInfoWrapper>(array) {
669 }
670
SetProperties(Handle<String> name,int start_position,int end_position,Handle<SharedFunctionInfo> info)671 void SetProperties(Handle<String> name, int start_position, int end_position,
672 Handle<SharedFunctionInfo> info) {
673 HandleScope scope;
674 this->SetField(kFunctionNameOffset_, name);
675 Handle<JSValue> info_holder = WrapInJSValue(*info);
676 this->SetField(kSharedInfoOffset_, info_holder);
677 this->SetSmiValueField(kStartPositionOffset_, start_position);
678 this->SetSmiValueField(kEndPositionOffset_, end_position);
679 }
GetInfo()680 Handle<SharedFunctionInfo> GetInfo() {
681 Object* element = this->GetField(kSharedInfoOffset_);
682 Handle<JSValue> value_wrapper(JSValue::cast(element));
683 Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
684 return Handle<SharedFunctionInfo>::cast(raw_result);
685 }
686
687 private:
688 static const int kFunctionNameOffset_ = 0;
689 static const int kStartPositionOffset_ = 1;
690 static const int kEndPositionOffset_ = 2;
691 static const int kSharedInfoOffset_ = 3;
692 static const int kSize_ = 4;
693
694 friend class JSArrayBasedStruct<SharedInfoWrapper>;
695 };
696
697
698 class FunctionInfoListener {
699 public:
FunctionInfoListener()700 FunctionInfoListener() {
701 current_parent_index_ = -1;
702 len_ = 0;
703 result_ = FACTORY->NewJSArray(10);
704 }
705
FunctionStarted(FunctionLiteral * fun)706 void FunctionStarted(FunctionLiteral* fun) {
707 HandleScope scope;
708 FunctionInfoWrapper info = FunctionInfoWrapper::Create();
709 info.SetInitialProperties(fun->name(), fun->start_position(),
710 fun->end_position(), fun->num_parameters(),
711 current_parent_index_);
712 current_parent_index_ = len_;
713 SetElementNonStrict(result_, len_, info.GetJSArray());
714 len_++;
715 }
716
FunctionDone()717 void FunctionDone() {
718 HandleScope scope;
719 FunctionInfoWrapper info =
720 FunctionInfoWrapper::cast(
721 result_->GetElementNoExceptionThrown(current_parent_index_));
722 current_parent_index_ = info.GetParentIndex();
723 }
724
725 // Saves only function code, because for a script function we
726 // may never create a SharedFunctionInfo object.
FunctionCode(Handle<Code> function_code)727 void FunctionCode(Handle<Code> function_code) {
728 FunctionInfoWrapper info =
729 FunctionInfoWrapper::cast(
730 result_->GetElementNoExceptionThrown(current_parent_index_));
731 info.SetFunctionCode(function_code, Handle<Object>(HEAP->null_value()));
732 }
733
734 // Saves full information about a function: its code, its scope info
735 // and a SharedFunctionInfo object.
FunctionInfo(Handle<SharedFunctionInfo> shared,Scope * scope)736 void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope) {
737 if (!shared->IsSharedFunctionInfo()) {
738 return;
739 }
740 FunctionInfoWrapper info =
741 FunctionInfoWrapper::cast(
742 result_->GetElementNoExceptionThrown(current_parent_index_));
743 info.SetFunctionCode(Handle<Code>(shared->code()),
744 Handle<Object>(shared->scope_info()));
745 info.SetSharedFunctionInfo(shared);
746
747 Handle<Object> scope_info_list(SerializeFunctionScope(scope));
748 info.SetOuterScopeInfo(scope_info_list);
749 }
750
GetResult()751 Handle<JSArray> GetResult() { return result_; }
752
753 private:
SerializeFunctionScope(Scope * scope)754 Object* SerializeFunctionScope(Scope* scope) {
755 HandleScope handle_scope;
756
757 Handle<JSArray> scope_info_list = FACTORY->NewJSArray(10);
758 int scope_info_length = 0;
759
760 // Saves some description of scope. It stores name and indexes of
761 // variables in the whole scope chain. Null-named slots delimit
762 // scopes of this chain.
763 Scope* outer_scope = scope->outer_scope();
764 if (outer_scope == NULL) {
765 return HEAP->undefined_value();
766 }
767 do {
768 ZoneList<Variable*> list(10);
769 outer_scope->CollectUsedVariables(&list);
770 int j = 0;
771 for (int i = 0; i < list.length(); i++) {
772 Variable* var1 = list[i];
773 Slot* slot = var1->AsSlot();
774 if (slot != NULL && slot->type() == Slot::CONTEXT) {
775 if (j != i) {
776 list[j] = var1;
777 }
778 j++;
779 }
780 }
781
782 // Sort it.
783 for (int k = 1; k < j; k++) {
784 int l = k;
785 for (int m = k + 1; m < j; m++) {
786 if (list[l]->AsSlot()->index() > list[m]->AsSlot()->index()) {
787 l = m;
788 }
789 }
790 list[k] = list[l];
791 }
792 for (int i = 0; i < j; i++) {
793 SetElementNonStrict(scope_info_list,
794 scope_info_length,
795 list[i]->name());
796 scope_info_length++;
797 SetElementNonStrict(
798 scope_info_list,
799 scope_info_length,
800 Handle<Smi>(Smi::FromInt(list[i]->AsSlot()->index())));
801 scope_info_length++;
802 }
803 SetElementNonStrict(scope_info_list,
804 scope_info_length,
805 Handle<Object>(HEAP->null_value()));
806 scope_info_length++;
807
808 outer_scope = outer_scope->outer_scope();
809 } while (outer_scope != NULL);
810
811 return *scope_info_list;
812 }
813
814 Handle<JSArray> result_;
815 int len_;
816 int current_parent_index_;
817 };
818
819
GatherCompileInfo(Handle<Script> script,Handle<String> source)820 JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
821 Handle<String> source) {
822 Isolate* isolate = Isolate::Current();
823 CompilationZoneScope zone_scope(DELETE_ON_EXIT);
824
825 FunctionInfoListener listener;
826 Handle<Object> original_source = Handle<Object>(script->source());
827 script->set_source(*source);
828 isolate->set_active_function_info_listener(&listener);
829 CompileScriptForTracker(isolate, script);
830 isolate->set_active_function_info_listener(NULL);
831 script->set_source(*original_source);
832
833 return *(listener.GetResult());
834 }
835
836
WrapSharedFunctionInfos(Handle<JSArray> array)837 void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
838 HandleScope scope;
839 int len = Smi::cast(array->length())->value();
840 for (int i = 0; i < len; i++) {
841 Handle<SharedFunctionInfo> info(
842 SharedFunctionInfo::cast(array->GetElementNoExceptionThrown(i)));
843 SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create();
844 Handle<String> name_handle(String::cast(info->name()));
845 info_wrapper.SetProperties(name_handle, info->start_position(),
846 info->end_position(), info);
847 SetElementNonStrict(array, i, info_wrapper.GetJSArray());
848 }
849 }
850
851
852 // Visitor that collects all references to a particular code object,
853 // including "CODE_TARGET" references in other code objects.
854 // It works in context of ZoneScope.
855 class ReferenceCollectorVisitor : public ObjectVisitor {
856 public:
ReferenceCollectorVisitor(Code * original)857 explicit ReferenceCollectorVisitor(Code* original)
858 : original_(original), rvalues_(10), reloc_infos_(10), code_entries_(10) {
859 }
860
VisitPointers(Object ** start,Object ** end)861 virtual void VisitPointers(Object** start, Object** end) {
862 for (Object** p = start; p < end; p++) {
863 if (*p == original_) {
864 rvalues_.Add(p);
865 }
866 }
867 }
868
VisitCodeEntry(Address entry)869 virtual void VisitCodeEntry(Address entry) {
870 if (Code::GetObjectFromEntryAddress(entry) == original_) {
871 code_entries_.Add(entry);
872 }
873 }
874
VisitCodeTarget(RelocInfo * rinfo)875 virtual void VisitCodeTarget(RelocInfo* rinfo) {
876 if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
877 Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
878 reloc_infos_.Add(*rinfo);
879 }
880 }
881
VisitDebugTarget(RelocInfo * rinfo)882 virtual void VisitDebugTarget(RelocInfo* rinfo) {
883 VisitCodeTarget(rinfo);
884 }
885
886 // Post-visiting method that iterates over all collected references and
887 // modifies them.
Replace(Code * substitution)888 void Replace(Code* substitution) {
889 for (int i = 0; i < rvalues_.length(); i++) {
890 *(rvalues_[i]) = substitution;
891 }
892 Address substitution_entry = substitution->instruction_start();
893 for (int i = 0; i < reloc_infos_.length(); i++) {
894 reloc_infos_[i].set_target_address(substitution_entry);
895 }
896 for (int i = 0; i < code_entries_.length(); i++) {
897 Address entry = code_entries_[i];
898 Memory::Address_at(entry) = substitution_entry;
899 }
900 }
901
902 private:
903 Code* original_;
904 ZoneList<Object**> rvalues_;
905 ZoneList<RelocInfo> reloc_infos_;
906 ZoneList<Address> code_entries_;
907 };
908
909
910 // Finds all references to original and replaces them with substitution.
ReplaceCodeObject(Code * original,Code * substitution)911 static void ReplaceCodeObject(Code* original, Code* substitution) {
912 ASSERT(!HEAP->InNewSpace(substitution));
913
914 AssertNoAllocation no_allocations_please;
915
916 // A zone scope for ReferenceCollectorVisitor.
917 ZoneScope scope(DELETE_ON_EXIT);
918
919 ReferenceCollectorVisitor visitor(original);
920
921 // Iterate over all roots. Stack frames may have pointer into original code,
922 // so temporary replace the pointers with offset numbers
923 // in prologue/epilogue.
924 {
925 HEAP->IterateStrongRoots(&visitor, VISIT_ALL);
926 }
927
928 // Now iterate over all pointers of all objects, including code_target
929 // implicit pointers.
930 HeapIterator iterator;
931 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
932 obj->Iterate(&visitor);
933 }
934
935 visitor.Replace(substitution);
936 }
937
938
939 // Check whether the code is natural function code (not a lazy-compile stub
940 // code).
IsJSFunctionCode(Code * code)941 static bool IsJSFunctionCode(Code* code) {
942 return code->kind() == Code::FUNCTION;
943 }
944
945
946 // Returns true if an instance of candidate were inlined into function's code.
IsInlined(JSFunction * function,SharedFunctionInfo * candidate)947 static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) {
948 AssertNoAllocation no_gc;
949
950 if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
951
952 DeoptimizationInputData* data =
953 DeoptimizationInputData::cast(function->code()->deoptimization_data());
954
955 if (data == HEAP->empty_fixed_array()) return false;
956
957 FixedArray* literals = data->LiteralArray();
958
959 int inlined_count = data->InlinedFunctionCount()->value();
960 for (int i = 0; i < inlined_count; ++i) {
961 JSFunction* inlined = JSFunction::cast(literals->get(i));
962 if (inlined->shared() == candidate) return true;
963 }
964
965 return false;
966 }
967
968
969 class DependentFunctionsDeoptimizingVisitor : public OptimizedFunctionVisitor {
970 public:
DependentFunctionsDeoptimizingVisitor(SharedFunctionInfo * function_info)971 explicit DependentFunctionsDeoptimizingVisitor(
972 SharedFunctionInfo* function_info)
973 : function_info_(function_info) {}
974
EnterContext(Context * context)975 virtual void EnterContext(Context* context) {
976 }
977
VisitFunction(JSFunction * function)978 virtual void VisitFunction(JSFunction* function) {
979 if (function->shared() == function_info_ ||
980 IsInlined(function, function_info_)) {
981 Deoptimizer::DeoptimizeFunction(function);
982 }
983 }
984
LeaveContext(Context * context)985 virtual void LeaveContext(Context* context) {
986 }
987
988 private:
989 SharedFunctionInfo* function_info_;
990 };
991
992
DeoptimizeDependentFunctions(SharedFunctionInfo * function_info)993 static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
994 AssertNoAllocation no_allocation;
995
996 DependentFunctionsDeoptimizingVisitor visitor(function_info);
997 Deoptimizer::VisitAllOptimizedFunctions(&visitor);
998 }
999
1000
ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,Handle<JSArray> shared_info_array)1001 MaybeObject* LiveEdit::ReplaceFunctionCode(
1002 Handle<JSArray> new_compile_info_array,
1003 Handle<JSArray> shared_info_array) {
1004 HandleScope scope;
1005
1006 if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
1007 return Isolate::Current()->ThrowIllegalOperation();
1008 }
1009
1010 FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
1011 SharedInfoWrapper shared_info_wrapper(shared_info_array);
1012
1013 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1014
1015 if (IsJSFunctionCode(shared_info->code())) {
1016 Handle<Code> code = compile_info_wrapper.GetFunctionCode();
1017 ReplaceCodeObject(shared_info->code(), *code);
1018 Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
1019 if (code_scope_info->IsFixedArray()) {
1020 shared_info->set_scope_info(SerializedScopeInfo::cast(*code_scope_info));
1021 }
1022 }
1023
1024 if (shared_info->debug_info()->IsDebugInfo()) {
1025 Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
1026 Handle<Code> new_original_code =
1027 FACTORY->CopyCode(compile_info_wrapper.GetFunctionCode());
1028 debug_info->set_original_code(*new_original_code);
1029 }
1030
1031 int start_position = compile_info_wrapper.GetStartPosition();
1032 int end_position = compile_info_wrapper.GetEndPosition();
1033 shared_info->set_start_position(start_position);
1034 shared_info->set_end_position(end_position);
1035
1036 shared_info->set_construct_stub(
1037 Isolate::Current()->builtins()->builtin(
1038 Builtins::kJSConstructStubGeneric));
1039
1040 DeoptimizeDependentFunctions(*shared_info);
1041 Isolate::Current()->compilation_cache()->Remove(shared_info);
1042
1043 return HEAP->undefined_value();
1044 }
1045
1046
FunctionSourceUpdated(Handle<JSArray> shared_info_array)1047 MaybeObject* LiveEdit::FunctionSourceUpdated(
1048 Handle<JSArray> shared_info_array) {
1049 HandleScope scope;
1050
1051 if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
1052 return Isolate::Current()->ThrowIllegalOperation();
1053 }
1054
1055 SharedInfoWrapper shared_info_wrapper(shared_info_array);
1056 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1057
1058 DeoptimizeDependentFunctions(*shared_info);
1059 Isolate::Current()->compilation_cache()->Remove(shared_info);
1060
1061 return HEAP->undefined_value();
1062 }
1063
1064
SetFunctionScript(Handle<JSValue> function_wrapper,Handle<Object> script_handle)1065 void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
1066 Handle<Object> script_handle) {
1067 Handle<SharedFunctionInfo> shared_info =
1068 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(function_wrapper));
1069 shared_info->set_script(*script_handle);
1070
1071 Isolate::Current()->compilation_cache()->Remove(shared_info);
1072 }
1073
1074
1075 // For a script text change (defined as position_change_array), translates
1076 // position in unchanged text to position in changed text.
1077 // Text change is a set of non-overlapping regions in text, that have changed
1078 // their contents and length. It is specified as array of groups of 3 numbers:
1079 // (change_begin, change_end, change_end_new_position).
1080 // Each group describes a change in text; groups are sorted by change_begin.
1081 // Only position in text beyond any changes may be successfully translated.
1082 // If a positions is inside some region that changed, result is currently
1083 // undefined.
TranslatePosition(int original_position,Handle<JSArray> position_change_array)1084 static int TranslatePosition(int original_position,
1085 Handle<JSArray> position_change_array) {
1086 int position_diff = 0;
1087 int array_len = Smi::cast(position_change_array->length())->value();
1088 // TODO(635): binary search may be used here
1089 for (int i = 0; i < array_len; i += 3) {
1090 Object* element = position_change_array->GetElementNoExceptionThrown(i);
1091 int chunk_start = Smi::cast(element)->value();
1092 if (original_position < chunk_start) {
1093 break;
1094 }
1095 element = position_change_array->GetElementNoExceptionThrown(i + 1);
1096 int chunk_end = Smi::cast(element)->value();
1097 // Position mustn't be inside a chunk.
1098 ASSERT(original_position >= chunk_end);
1099 element = position_change_array->GetElementNoExceptionThrown(i + 2);
1100 int chunk_changed_end = Smi::cast(element)->value();
1101 position_diff = chunk_changed_end - chunk_end;
1102 }
1103
1104 return original_position + position_diff;
1105 }
1106
1107
1108 // Auto-growing buffer for writing relocation info code section. This buffer
1109 // is a simplified version of buffer from Assembler. Unlike Assembler, this
1110 // class is platform-independent and it works without dealing with instructions.
1111 // As specified by RelocInfo format, the buffer is filled in reversed order:
1112 // from upper to lower addresses.
1113 // It uses NewArray/DeleteArray for memory management.
1114 class RelocInfoBuffer {
1115 public:
RelocInfoBuffer(int buffer_initial_capicity,byte * pc)1116 RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
1117 buffer_size_ = buffer_initial_capicity + kBufferGap;
1118 buffer_ = NewArray<byte>(buffer_size_);
1119
1120 reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
1121 }
~RelocInfoBuffer()1122 ~RelocInfoBuffer() {
1123 DeleteArray(buffer_);
1124 }
1125
1126 // As specified by RelocInfo format, the buffer is filled in reversed order:
1127 // from upper to lower addresses.
Write(const RelocInfo * rinfo)1128 void Write(const RelocInfo* rinfo) {
1129 if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
1130 Grow();
1131 }
1132 reloc_info_writer_.Write(rinfo);
1133 }
1134
GetResult()1135 Vector<byte> GetResult() {
1136 // Return the bytes from pos up to end of buffer.
1137 int result_size =
1138 static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
1139 return Vector<byte>(reloc_info_writer_.pos(), result_size);
1140 }
1141
1142 private:
Grow()1143 void Grow() {
1144 // Compute new buffer size.
1145 int new_buffer_size;
1146 if (buffer_size_ < 2 * KB) {
1147 new_buffer_size = 4 * KB;
1148 } else {
1149 new_buffer_size = 2 * buffer_size_;
1150 }
1151 // Some internal data structures overflow for very large buffers,
1152 // they must ensure that kMaximalBufferSize is not too large.
1153 if (new_buffer_size > kMaximalBufferSize) {
1154 V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
1155 }
1156
1157 // Setup new buffer.
1158 byte* new_buffer = NewArray<byte>(new_buffer_size);
1159
1160 // Copy the data.
1161 int curently_used_size =
1162 static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
1163 memmove(new_buffer + new_buffer_size - curently_used_size,
1164 reloc_info_writer_.pos(), curently_used_size);
1165
1166 reloc_info_writer_.Reposition(
1167 new_buffer + new_buffer_size - curently_used_size,
1168 reloc_info_writer_.last_pc());
1169
1170 DeleteArray(buffer_);
1171 buffer_ = new_buffer;
1172 buffer_size_ = new_buffer_size;
1173 }
1174
1175 RelocInfoWriter reloc_info_writer_;
1176 byte* buffer_;
1177 int buffer_size_;
1178
1179 static const int kBufferGap = RelocInfoWriter::kMaxSize;
1180 static const int kMaximalBufferSize = 512*MB;
1181 };
1182
1183 // Patch positions in code (changes relocation info section) and possibly
1184 // returns new instance of code.
PatchPositionsInCode(Handle<Code> code,Handle<JSArray> position_change_array)1185 static Handle<Code> PatchPositionsInCode(Handle<Code> code,
1186 Handle<JSArray> position_change_array) {
1187
1188 RelocInfoBuffer buffer_writer(code->relocation_size(),
1189 code->instruction_start());
1190
1191 {
1192 AssertNoAllocation no_allocations_please;
1193 for (RelocIterator it(*code); !it.done(); it.next()) {
1194 RelocInfo* rinfo = it.rinfo();
1195 if (RelocInfo::IsPosition(rinfo->rmode())) {
1196 int position = static_cast<int>(rinfo->data());
1197 int new_position = TranslatePosition(position,
1198 position_change_array);
1199 if (position != new_position) {
1200 RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position);
1201 buffer_writer.Write(&info_copy);
1202 continue;
1203 }
1204 }
1205 buffer_writer.Write(it.rinfo());
1206 }
1207 }
1208
1209 Vector<byte> buffer = buffer_writer.GetResult();
1210
1211 if (buffer.length() == code->relocation_size()) {
1212 // Simply patch relocation area of code.
1213 memcpy(code->relocation_start(), buffer.start(), buffer.length());
1214 return code;
1215 } else {
1216 // Relocation info section now has different size. We cannot simply
1217 // rewrite it inside code object. Instead we have to create a new
1218 // code object.
1219 Handle<Code> result(FACTORY->CopyCode(code, buffer));
1220 return result;
1221 }
1222 }
1223
1224
PatchFunctionPositions(Handle<JSArray> shared_info_array,Handle<JSArray> position_change_array)1225 MaybeObject* LiveEdit::PatchFunctionPositions(
1226 Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) {
1227
1228 if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
1229 return Isolate::Current()->ThrowIllegalOperation();
1230 }
1231
1232 SharedInfoWrapper shared_info_wrapper(shared_info_array);
1233 Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
1234
1235 int old_function_start = info->start_position();
1236 int new_function_start = TranslatePosition(old_function_start,
1237 position_change_array);
1238 int new_function_end = TranslatePosition(info->end_position(),
1239 position_change_array);
1240 int new_function_token_pos =
1241 TranslatePosition(info->function_token_position(), position_change_array);
1242
1243 info->set_start_position(new_function_start);
1244 info->set_end_position(new_function_end);
1245 info->set_function_token_position(new_function_token_pos);
1246
1247 if (IsJSFunctionCode(info->code())) {
1248 // Patch relocation info section of the code.
1249 Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
1250 position_change_array);
1251 if (*patched_code != info->code()) {
1252 // Replace all references to the code across the heap. In particular,
1253 // some stubs may refer to this code and this code may be being executed
1254 // on stack (it is safe to substitute the code object on stack, because
1255 // we only change the structure of rinfo and leave instructions
1256 // untouched).
1257 ReplaceCodeObject(info->code(), *patched_code);
1258 }
1259 }
1260
1261 return HEAP->undefined_value();
1262 }
1263
1264
CreateScriptCopy(Handle<Script> original)1265 static Handle<Script> CreateScriptCopy(Handle<Script> original) {
1266 Handle<String> original_source(String::cast(original->source()));
1267
1268 Handle<Script> copy = FACTORY->NewScript(original_source);
1269
1270 copy->set_name(original->name());
1271 copy->set_line_offset(original->line_offset());
1272 copy->set_column_offset(original->column_offset());
1273 copy->set_data(original->data());
1274 copy->set_type(original->type());
1275 copy->set_context_data(original->context_data());
1276 copy->set_compilation_type(original->compilation_type());
1277 copy->set_eval_from_shared(original->eval_from_shared());
1278 copy->set_eval_from_instructions_offset(
1279 original->eval_from_instructions_offset());
1280
1281 return copy;
1282 }
1283
1284
ChangeScriptSource(Handle<Script> original_script,Handle<String> new_source,Handle<Object> old_script_name)1285 Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script,
1286 Handle<String> new_source,
1287 Handle<Object> old_script_name) {
1288 Handle<Object> old_script_object;
1289 if (old_script_name->IsString()) {
1290 Handle<Script> old_script = CreateScriptCopy(original_script);
1291 old_script->set_name(String::cast(*old_script_name));
1292 old_script_object = old_script;
1293 Isolate::Current()->debugger()->OnAfterCompile(
1294 old_script, Debugger::SEND_WHEN_DEBUGGING);
1295 } else {
1296 old_script_object = Handle<Object>(HEAP->null_value());
1297 }
1298
1299 original_script->set_source(*new_source);
1300
1301 // Drop line ends so that they will be recalculated.
1302 original_script->set_line_ends(HEAP->undefined_value());
1303
1304 return *old_script_object;
1305 }
1306
1307
1308
ReplaceRefToNestedFunction(Handle<JSValue> parent_function_wrapper,Handle<JSValue> orig_function_wrapper,Handle<JSValue> subst_function_wrapper)1309 void LiveEdit::ReplaceRefToNestedFunction(
1310 Handle<JSValue> parent_function_wrapper,
1311 Handle<JSValue> orig_function_wrapper,
1312 Handle<JSValue> subst_function_wrapper) {
1313
1314 Handle<SharedFunctionInfo> parent_shared =
1315 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(parent_function_wrapper));
1316 Handle<SharedFunctionInfo> orig_shared =
1317 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(orig_function_wrapper));
1318 Handle<SharedFunctionInfo> subst_shared =
1319 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(subst_function_wrapper));
1320
1321 for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
1322 if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
1323 if (it.rinfo()->target_object() == *orig_shared) {
1324 it.rinfo()->set_target_object(*subst_shared);
1325 }
1326 }
1327 }
1328 }
1329
1330
1331 // Check an activation against list of functions. If there is a function
1332 // 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)1333 static bool CheckActivation(Handle<JSArray> shared_info_array,
1334 Handle<JSArray> result,
1335 StackFrame* frame,
1336 LiveEdit::FunctionPatchabilityStatus status) {
1337 if (!frame->is_java_script()) return false;
1338
1339 Handle<JSFunction> function(
1340 JSFunction::cast(JavaScriptFrame::cast(frame)->function()));
1341
1342 int len = Smi::cast(shared_info_array->length())->value();
1343 for (int i = 0; i < len; i++) {
1344 JSValue* wrapper =
1345 JSValue::cast(shared_info_array->GetElementNoExceptionThrown(i));
1346 Handle<SharedFunctionInfo> shared(
1347 SharedFunctionInfo::cast(wrapper->value()));
1348
1349 if (function->shared() == *shared || IsInlined(*function, *shared)) {
1350 SetElementNonStrict(result, i, Handle<Smi>(Smi::FromInt(status)));
1351 return true;
1352 }
1353 }
1354 return false;
1355 }
1356
1357
1358 // Iterates over handler chain and removes all elements that are inside
1359 // frames being dropped.
FixTryCatchHandler(StackFrame * top_frame,StackFrame * bottom_frame)1360 static bool FixTryCatchHandler(StackFrame* top_frame,
1361 StackFrame* bottom_frame) {
1362 Address* pointer_address =
1363 &Memory::Address_at(Isolate::Current()->get_address_from_id(
1364 Isolate::k_handler_address));
1365
1366 while (*pointer_address < top_frame->sp()) {
1367 pointer_address = &Memory::Address_at(*pointer_address);
1368 }
1369 Address* above_frame_address = pointer_address;
1370 while (*pointer_address < bottom_frame->fp()) {
1371 pointer_address = &Memory::Address_at(*pointer_address);
1372 }
1373 bool change = *above_frame_address != *pointer_address;
1374 *above_frame_address = *pointer_address;
1375 return change;
1376 }
1377
1378
1379 // Removes specified range of frames from stack. There may be 1 or more
1380 // frames in range. Anyway the bottom frame is restarted rather than dropped,
1381 // and therefore has to be a JavaScript frame.
1382 // 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)1383 static const char* DropFrames(Vector<StackFrame*> frames,
1384 int top_frame_index,
1385 int bottom_js_frame_index,
1386 Debug::FrameDropMode* mode,
1387 Object*** restarter_frame_function_pointer) {
1388 if (!Debug::kFrameDropperSupported) {
1389 return "Stack manipulations are not supported in this architecture.";
1390 }
1391
1392 StackFrame* pre_top_frame = frames[top_frame_index - 1];
1393 StackFrame* top_frame = frames[top_frame_index];
1394 StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
1395
1396 ASSERT(bottom_js_frame->is_java_script());
1397
1398 // Check the nature of the top frame.
1399 Isolate* isolate = Isolate::Current();
1400 Code* pre_top_frame_code = pre_top_frame->LookupCode();
1401 if (pre_top_frame_code->is_inline_cache_stub() &&
1402 pre_top_frame_code->ic_state() == DEBUG_BREAK) {
1403 // OK, we can drop inline cache calls.
1404 *mode = Debug::FRAME_DROPPED_IN_IC_CALL;
1405 } else if (pre_top_frame_code ==
1406 isolate->debug()->debug_break_slot()) {
1407 // OK, we can drop debug break slot.
1408 *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
1409 } else if (pre_top_frame_code ==
1410 isolate->builtins()->builtin(
1411 Builtins::kFrameDropper_LiveEdit)) {
1412 // OK, we can drop our own code.
1413 *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
1414 } else if (pre_top_frame_code->kind() == Code::STUB &&
1415 pre_top_frame_code->major_key()) {
1416 // Entry from our unit tests, it's fine, we support this case.
1417 *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
1418 } else {
1419 return "Unknown structure of stack above changing function";
1420 }
1421
1422 Address unused_stack_top = top_frame->sp();
1423 Address unused_stack_bottom = bottom_js_frame->fp()
1424 - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame.
1425 + kPointerSize; // Bigger address end is exclusive.
1426
1427 if (unused_stack_top > unused_stack_bottom) {
1428 return "Not enough space for frame dropper frame";
1429 }
1430
1431 // Committing now. After this point we should return only NULL value.
1432
1433 FixTryCatchHandler(pre_top_frame, bottom_js_frame);
1434 // Make sure FixTryCatchHandler is idempotent.
1435 ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
1436
1437 Handle<Code> code = Isolate::Current()->builtins()->FrameDropper_LiveEdit();
1438 top_frame->set_pc(code->entry());
1439 pre_top_frame->SetCallerFp(bottom_js_frame->fp());
1440
1441 *restarter_frame_function_pointer =
1442 Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
1443
1444 ASSERT((**restarter_frame_function_pointer)->IsJSFunction());
1445
1446 for (Address a = unused_stack_top;
1447 a < unused_stack_bottom;
1448 a += kPointerSize) {
1449 Memory::Object_at(a) = Smi::FromInt(0);
1450 }
1451
1452 return NULL;
1453 }
1454
1455
IsDropableFrame(StackFrame * frame)1456 static bool IsDropableFrame(StackFrame* frame) {
1457 return !frame->is_exit();
1458 }
1459
1460 // Fills result array with statuses of functions. Modifies the stack
1461 // removing all listed function if possible and if do_drop is true.
DropActivationsInActiveThread(Handle<JSArray> shared_info_array,Handle<JSArray> result,bool do_drop)1462 static const char* DropActivationsInActiveThread(
1463 Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
1464 Debug* debug = Isolate::Current()->debug();
1465 ZoneScope scope(DELETE_ON_EXIT);
1466 Vector<StackFrame*> frames = CreateStackMap();
1467
1468 int array_len = Smi::cast(shared_info_array->length())->value();
1469
1470 int top_frame_index = -1;
1471 int frame_index = 0;
1472 for (; frame_index < frames.length(); frame_index++) {
1473 StackFrame* frame = frames[frame_index];
1474 if (frame->id() == debug->break_frame_id()) {
1475 top_frame_index = frame_index;
1476 break;
1477 }
1478 if (CheckActivation(shared_info_array, result, frame,
1479 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
1480 // We are still above break_frame. It is not a target frame,
1481 // it is a problem.
1482 return "Debugger mark-up on stack is not found";
1483 }
1484 }
1485
1486 if (top_frame_index == -1) {
1487 // We haven't found break frame, but no function is blocking us anyway.
1488 return NULL;
1489 }
1490
1491 bool target_frame_found = false;
1492 int bottom_js_frame_index = top_frame_index;
1493 bool c_code_found = false;
1494
1495 for (; frame_index < frames.length(); frame_index++) {
1496 StackFrame* frame = frames[frame_index];
1497 if (!IsDropableFrame(frame)) {
1498 c_code_found = true;
1499 break;
1500 }
1501 if (CheckActivation(shared_info_array, result, frame,
1502 LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
1503 target_frame_found = true;
1504 bottom_js_frame_index = frame_index;
1505 }
1506 }
1507
1508 if (c_code_found) {
1509 // There is a C frames on stack. Check that there are no target frames
1510 // below them.
1511 for (; frame_index < frames.length(); frame_index++) {
1512 StackFrame* frame = frames[frame_index];
1513 if (frame->is_java_script()) {
1514 if (CheckActivation(shared_info_array, result, frame,
1515 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
1516 // Cannot drop frame under C frames.
1517 return NULL;
1518 }
1519 }
1520 }
1521 }
1522
1523 if (!do_drop) {
1524 // We are in check-only mode.
1525 return NULL;
1526 }
1527
1528 if (!target_frame_found) {
1529 // Nothing to drop.
1530 return NULL;
1531 }
1532
1533 Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED;
1534 Object** restarter_frame_function_pointer = NULL;
1535 const char* error_message = DropFrames(frames, top_frame_index,
1536 bottom_js_frame_index, &drop_mode,
1537 &restarter_frame_function_pointer);
1538
1539 if (error_message != NULL) {
1540 return error_message;
1541 }
1542
1543 // Adjust break_frame after some frames has been dropped.
1544 StackFrame::Id new_id = StackFrame::NO_ID;
1545 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
1546 if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
1547 new_id = frames[i]->id();
1548 break;
1549 }
1550 }
1551 debug->FramesHaveBeenDropped(new_id, drop_mode,
1552 restarter_frame_function_pointer);
1553
1554 // Replace "blocked on active" with "replaced on active" status.
1555 for (int i = 0; i < array_len; i++) {
1556 if (result->GetElement(i) ==
1557 Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
1558 Handle<Object> replaced(
1559 Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK));
1560 SetElementNonStrict(result, i, replaced);
1561 }
1562 }
1563 return NULL;
1564 }
1565
1566
1567 class InactiveThreadActivationsChecker : public ThreadVisitor {
1568 public:
InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,Handle<JSArray> result)1569 InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
1570 Handle<JSArray> result)
1571 : shared_info_array_(shared_info_array), result_(result),
1572 has_blocked_functions_(false) {
1573 }
VisitThread(Isolate * isolate,ThreadLocalTop * top)1574 void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
1575 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
1576 has_blocked_functions_ |= CheckActivation(
1577 shared_info_array_, result_, it.frame(),
1578 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
1579 }
1580 }
HasBlockedFunctions()1581 bool HasBlockedFunctions() {
1582 return has_blocked_functions_;
1583 }
1584
1585 private:
1586 Handle<JSArray> shared_info_array_;
1587 Handle<JSArray> result_;
1588 bool has_blocked_functions_;
1589 };
1590
1591
CheckAndDropActivations(Handle<JSArray> shared_info_array,bool do_drop)1592 Handle<JSArray> LiveEdit::CheckAndDropActivations(
1593 Handle<JSArray> shared_info_array, bool do_drop) {
1594 int len = Smi::cast(shared_info_array->length())->value();
1595
1596 Handle<JSArray> result = FACTORY->NewJSArray(len);
1597
1598 // Fill the default values.
1599 for (int i = 0; i < len; i++) {
1600 SetElementNonStrict(
1601 result,
1602 i,
1603 Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH)));
1604 }
1605
1606
1607 // First check inactive threads. Fail if some functions are blocked there.
1608 InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
1609 result);
1610 Isolate::Current()->thread_manager()->IterateArchivedThreads(
1611 &inactive_threads_checker);
1612 if (inactive_threads_checker.HasBlockedFunctions()) {
1613 return result;
1614 }
1615
1616 // Try to drop activations from the current stack.
1617 const char* error_message =
1618 DropActivationsInActiveThread(shared_info_array, result, do_drop);
1619 if (error_message != NULL) {
1620 // Add error message as an array extra element.
1621 Vector<const char> vector_message(error_message, StrLength(error_message));
1622 Handle<String> str = FACTORY->NewStringFromAscii(vector_message);
1623 SetElementNonStrict(result, len, str);
1624 }
1625 return result;
1626 }
1627
1628
LiveEditFunctionTracker(Isolate * isolate,FunctionLiteral * fun)1629 LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
1630 FunctionLiteral* fun)
1631 : isolate_(isolate) {
1632 if (isolate_->active_function_info_listener() != NULL) {
1633 isolate_->active_function_info_listener()->FunctionStarted(fun);
1634 }
1635 }
1636
1637
~LiveEditFunctionTracker()1638 LiveEditFunctionTracker::~LiveEditFunctionTracker() {
1639 if (isolate_->active_function_info_listener() != NULL) {
1640 isolate_->active_function_info_listener()->FunctionDone();
1641 }
1642 }
1643
1644
RecordFunctionInfo(Handle<SharedFunctionInfo> info,FunctionLiteral * lit)1645 void LiveEditFunctionTracker::RecordFunctionInfo(
1646 Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
1647 if (isolate_->active_function_info_listener() != NULL) {
1648 isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope());
1649 }
1650 }
1651
1652
RecordRootFunctionInfo(Handle<Code> code)1653 void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
1654 isolate_->active_function_info_listener()->FunctionCode(code);
1655 }
1656
1657
IsActive(Isolate * isolate)1658 bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
1659 return isolate->active_function_info_listener() != NULL;
1660 }
1661
1662
1663 #else // ENABLE_DEBUGGER_SUPPORT
1664
1665 // This ifdef-else-endif section provides working or stub implementation of
1666 // LiveEditFunctionTracker.
1667 LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
1668 FunctionLiteral* fun) {
1669 }
1670
1671
1672 LiveEditFunctionTracker::~LiveEditFunctionTracker() {
1673 }
1674
1675
1676 void LiveEditFunctionTracker::RecordFunctionInfo(
1677 Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
1678 }
1679
1680
1681 void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
1682 }
1683
1684
1685 bool LiveEditFunctionTracker::IsActive() {
1686 return false;
1687 }
1688
1689 #endif // ENABLE_DEBUGGER_SUPPORT
1690
1691
1692
1693 } } // namespace v8::internal
1694