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