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