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