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/assembler-inl.h" 8 #include "src/ast/scopes.h" 9 #include "src/code-stubs.h" 10 #include "src/compilation-cache.h" 11 #include "src/compiler.h" 12 #include "src/debug/debug.h" 13 #include "src/deoptimizer.h" 14 #include "src/frames-inl.h" 15 #include "src/global-handles.h" 16 #include "src/isolate-inl.h" 17 #include "src/messages.h" 18 #include "src/objects-inl.h" 19 #include "src/source-position-table.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 void FunctionInfoWrapper::SetInitialProperties(Handle<String> name, 610 int start_position, 611 int end_position, int param_num, 612 int parent_index, 613 int function_literal_id) { 614 HandleScope scope(isolate()); 615 this->SetField(kFunctionNameOffset_, name); 616 this->SetSmiValueField(kStartPositionOffset_, start_position); 617 this->SetSmiValueField(kEndPositionOffset_, end_position); 618 this->SetSmiValueField(kParamNumOffset_, param_num); 619 this->SetSmiValueField(kParentIndexOffset_, parent_index); 620 this->SetSmiValueField(kFunctionLiteralIdOffset_, function_literal_id); 621 } 622 623 void FunctionInfoWrapper::SetSharedFunctionInfo( 624 Handle<SharedFunctionInfo> info) { 625 Handle<JSValue> info_holder = WrapInJSValue(info); 626 this->SetField(kSharedFunctionInfoOffset_, info_holder); 627 } 628 629 Handle<SharedFunctionInfo> FunctionInfoWrapper::GetSharedFunctionInfo() { 630 Handle<Object> element = this->GetField(kSharedFunctionInfoOffset_); 631 Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element); 632 Handle<Object> raw_result = UnwrapJSValue(value_wrapper); 633 CHECK(raw_result->IsSharedFunctionInfo()); 634 return Handle<SharedFunctionInfo>::cast(raw_result); 635 } 636 637 void SharedInfoWrapper::SetProperties(Handle<String> name, 638 int start_position, 639 int end_position, 640 Handle<SharedFunctionInfo> info) { 641 HandleScope scope(isolate()); 642 this->SetField(kFunctionNameOffset_, name); 643 Handle<JSValue> info_holder = WrapInJSValue(info); 644 this->SetField(kSharedInfoOffset_, info_holder); 645 this->SetSmiValueField(kStartPositionOffset_, start_position); 646 this->SetSmiValueField(kEndPositionOffset_, end_position); 647 } 648 649 650 Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() { 651 Handle<Object> element = this->GetField(kSharedInfoOffset_); 652 Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element); 653 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper); 654 } 655 656 657 void LiveEdit::InitializeThreadLocal(Debug* debug) { 658 debug->thread_local_.restart_fp_ = 0; 659 } 660 661 662 MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script, 663 Handle<String> source) { 664 Isolate* isolate = script->GetIsolate(); 665 666 MaybeHandle<JSArray> infos; 667 Handle<Object> original_source = 668 Handle<Object>(script->source(), isolate); 669 script->set_source(*source); 670 671 { 672 // Creating verbose TryCatch from public API is currently the only way to 673 // force code save location. We do not use this the object directly. 674 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); 675 try_catch.SetVerbose(true); 676 677 // A logical 'try' section. 678 infos = Compiler::CompileForLiveEdit(script); 679 } 680 681 // A logical 'catch' section. 682 Handle<JSObject> rethrow_exception; 683 if (isolate->has_pending_exception()) { 684 Handle<Object> exception(isolate->pending_exception(), isolate); 685 MessageLocation message_location = isolate->GetMessageLocation(); 686 687 isolate->clear_pending_message(); 688 isolate->clear_pending_exception(); 689 690 // If possible, copy positions from message object to exception object. 691 if (exception->IsJSObject() && !message_location.script().is_null()) { 692 rethrow_exception = Handle<JSObject>::cast(exception); 693 694 Factory* factory = isolate->factory(); 695 Handle<String> start_pos_key = factory->InternalizeOneByteString( 696 STATIC_CHAR_VECTOR("startPosition")); 697 Handle<String> end_pos_key = 698 factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("endPosition")); 699 Handle<String> script_obj_key = 700 factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptObject")); 701 Handle<Smi> start_pos( 702 Smi::FromInt(message_location.start_pos()), isolate); 703 Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate); 704 Handle<JSObject> script_obj = 705 Script::GetWrapper(message_location.script()); 706 Object::SetProperty(rethrow_exception, start_pos_key, start_pos, SLOPPY) 707 .Assert(); 708 Object::SetProperty(rethrow_exception, end_pos_key, end_pos, SLOPPY) 709 .Assert(); 710 Object::SetProperty(rethrow_exception, script_obj_key, script_obj, SLOPPY) 711 .Assert(); 712 } 713 } 714 715 // A logical 'finally' section. 716 script->set_source(*original_source); 717 718 if (rethrow_exception.is_null()) { 719 return infos.ToHandleChecked(); 720 } else { 721 return isolate->Throw<JSArray>(rethrow_exception); 722 } 723 } 724 725 // Finds all references to original and replaces them with substitution. 726 static void ReplaceCodeObject(Handle<Code> original, 727 Handle<Code> substitution) { 728 // Perform a full GC in order to ensure that we are not in the middle of an 729 // incremental marking phase when we are replacing the code object. 730 // Since we are not in an incremental marking phase we can write pointers 731 // to code objects (that are never in new space) without worrying about 732 // write barriers. 733 Heap* heap = original->GetHeap(); 734 HeapIterator iterator(heap, HeapIterator::kFilterUnreachable); 735 // Now iterate over all pointers of all objects, including code_target 736 // implicit pointers. 737 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 738 if (obj->IsJSFunction()) { 739 JSFunction* fun = JSFunction::cast(obj); 740 if (fun->code() == *original) fun->ReplaceCode(*substitution); 741 } else if (obj->IsSharedFunctionInfo()) { 742 SharedFunctionInfo* info = SharedFunctionInfo::cast(obj); 743 if (info->code() == *original) info->set_code(*substitution); 744 } 745 } 746 } 747 748 // Patch function feedback vector. 749 // The feedback vector is a cache for complex object boilerplates and for a 750 // native context. We must clean cached values, or if the structure of the 751 // vector itself changes we need to allocate a new one. 752 class FeedbackVectorFixer { 753 public: 754 static void PatchFeedbackVector(FunctionInfoWrapper* compile_info_wrapper, 755 Handle<SharedFunctionInfo> shared_info, 756 Isolate* isolate) { 757 // When feedback metadata changes, we have to create new array instances. 758 // Since we cannot create instances when iterating heap, we should first 759 // collect all functions and fix their literal arrays. 760 Handle<FixedArray> function_instances = 761 CollectJSFunctions(shared_info, isolate); 762 763 for (int i = 0; i < function_instances->length(); i++) { 764 Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i))); 765 Handle<Cell> new_cell = isolate->factory()->NewManyClosuresCell( 766 isolate->factory()->undefined_value()); 767 fun->set_feedback_vector_cell(*new_cell); 768 // Only create feedback vectors if we already have the metadata. 769 if (shared_info->is_compiled()) JSFunction::EnsureLiterals(fun); 770 } 771 } 772 773 private: 774 // Iterates all function instances in the HEAP that refers to the 775 // provided shared_info. 776 template<typename Visitor> 777 static void IterateJSFunctions(Handle<SharedFunctionInfo> shared_info, 778 Visitor* visitor) { 779 HeapIterator iterator(shared_info->GetHeap()); 780 for (HeapObject* obj = iterator.next(); obj != NULL; 781 obj = iterator.next()) { 782 if (obj->IsJSFunction()) { 783 JSFunction* function = JSFunction::cast(obj); 784 if (function->shared() == *shared_info) { 785 visitor->visit(function); 786 } 787 } 788 } 789 } 790 791 // Finds all instances of JSFunction that refers to the provided shared_info 792 // and returns array with them. 793 static Handle<FixedArray> CollectJSFunctions( 794 Handle<SharedFunctionInfo> shared_info, Isolate* isolate) { 795 CountVisitor count_visitor; 796 count_visitor.count = 0; 797 IterateJSFunctions(shared_info, &count_visitor); 798 int size = count_visitor.count; 799 800 Handle<FixedArray> result = isolate->factory()->NewFixedArray(size); 801 if (size > 0) { 802 CollectVisitor collect_visitor(result); 803 IterateJSFunctions(shared_info, &collect_visitor); 804 } 805 return result; 806 } 807 808 class CountVisitor { 809 public: 810 void visit(JSFunction* fun) { 811 count++; 812 } 813 int count; 814 }; 815 816 class CollectVisitor { 817 public: 818 explicit CollectVisitor(Handle<FixedArray> output) 819 : m_output(output), m_pos(0) {} 820 821 void visit(JSFunction* fun) { 822 m_output->set(m_pos, fun); 823 m_pos++; 824 } 825 private: 826 Handle<FixedArray> m_output; 827 int m_pos; 828 }; 829 }; 830 831 832 // Marks code that shares the same shared function info or has inlined 833 // code that shares the same function info. 834 class DependentFunctionMarker: public OptimizedFunctionVisitor { 835 public: 836 SharedFunctionInfo* shared_info_; 837 bool found_; 838 839 explicit DependentFunctionMarker(SharedFunctionInfo* shared_info) 840 : shared_info_(shared_info), found_(false) { } 841 842 virtual void EnterContext(Context* context) { } // Don't care. 843 virtual void LeaveContext(Context* context) { } // Don't care. 844 virtual void VisitFunction(JSFunction* function) { 845 // It should be guaranteed by the iterator that everything is optimized. 846 DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION); 847 if (function->Inlines(shared_info_)) { 848 // Mark the code for deoptimization. 849 function->code()->set_marked_for_deoptimization(true); 850 found_ = true; 851 } 852 } 853 }; 854 855 856 static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) { 857 DisallowHeapAllocation no_allocation; 858 DependentFunctionMarker marker(function_info); 859 // TODO(titzer): need to traverse all optimized code to find OSR code here. 860 Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker); 861 862 if (marker.found_) { 863 // Only go through with the deoptimization if something was found. 864 Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate()); 865 } 866 } 867 868 869 void LiveEdit::ReplaceFunctionCode( 870 Handle<JSArray> new_compile_info_array, 871 Handle<JSArray> shared_info_array) { 872 Isolate* isolate = new_compile_info_array->GetIsolate(); 873 874 FunctionInfoWrapper compile_info_wrapper(new_compile_info_array); 875 SharedInfoWrapper shared_info_wrapper(shared_info_array); 876 877 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); 878 Handle<SharedFunctionInfo> new_shared_info = 879 compile_info_wrapper.GetSharedFunctionInfo(); 880 881 if (shared_info->is_compiled()) { 882 // Take whatever code we can get from the new shared function info. We 883 // expect activations of neither the old bytecode nor old FCG code, since 884 // the lowest activation is going to be restarted. 885 Handle<Code> old_code(shared_info->code()); 886 Handle<Code> new_code(new_shared_info->code()); 887 // Clear old bytecode. This will trigger self-healing if we do not install 888 // new bytecode. 889 shared_info->ClearBytecodeArray(); 890 if (!shared_info->HasBaselineCode()) { 891 // Every function from this SFI is interpreted. 892 if (!new_shared_info->HasBaselineCode()) { 893 // We have newly compiled bytecode. Simply replace the old one. 894 shared_info->set_bytecode_array(new_shared_info->bytecode_array()); 895 } else { 896 // Rely on self-healing for places that used to run bytecode. 897 shared_info->ReplaceCode(*new_code); 898 } 899 } else { 900 // Functions from this SFI can be either interpreted or running FCG. 901 DCHECK(old_code->kind() == Code::FUNCTION); 902 if (new_shared_info->HasBytecodeArray()) { 903 // Start using new bytecode everywhere. 904 shared_info->set_bytecode_array(new_shared_info->bytecode_array()); 905 ReplaceCodeObject(old_code, 906 isolate->builtins()->InterpreterEntryTrampoline()); 907 } else { 908 // Start using new FCG code everywhere. 909 // Rely on self-healing for places that used to run bytecode. 910 DCHECK(new_code->kind() == Code::FUNCTION); 911 ReplaceCodeObject(old_code, new_code); 912 } 913 } 914 915 if (shared_info->HasDebugInfo()) { 916 // Existing break points will be re-applied. Reset the debug info here. 917 isolate->debug()->RemoveDebugInfoAndClearFromShared( 918 handle(shared_info->GetDebugInfo())); 919 } 920 shared_info->set_scope_info(new_shared_info->scope_info()); 921 shared_info->set_outer_scope_info(new_shared_info->outer_scope_info()); 922 shared_info->DisableOptimization(kLiveEdit); 923 // Update the type feedback vector, if needed. 924 Handle<FeedbackMetadata> new_feedback_metadata( 925 new_shared_info->feedback_metadata()); 926 shared_info->set_feedback_metadata(*new_feedback_metadata); 927 } else { 928 shared_info->set_feedback_metadata( 929 FeedbackMetadata::cast(isolate->heap()->empty_fixed_array())); 930 } 931 932 int start_position = compile_info_wrapper.GetStartPosition(); 933 int end_position = compile_info_wrapper.GetEndPosition(); 934 shared_info->set_start_position(start_position); 935 shared_info->set_end_position(end_position); 936 937 FeedbackVectorFixer::PatchFeedbackVector(&compile_info_wrapper, shared_info, 938 isolate); 939 940 DeoptimizeDependentFunctions(*shared_info); 941 isolate->compilation_cache()->Remove(shared_info); 942 } 943 944 void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array, 945 int new_function_literal_id) { 946 SharedInfoWrapper shared_info_wrapper(shared_info_array); 947 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); 948 949 shared_info->set_function_literal_id(new_function_literal_id); 950 DeoptimizeDependentFunctions(*shared_info); 951 shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info); 952 } 953 954 void LiveEdit::FixupScript(Handle<Script> script, int max_function_literal_id) { 955 Isolate* isolate = script->GetIsolate(); 956 Handle<FixedArray> old_infos(script->shared_function_infos(), isolate); 957 Handle<FixedArray> new_infos( 958 isolate->factory()->NewFixedArray(max_function_literal_id + 1)); 959 script->set_shared_function_infos(*new_infos); 960 SharedFunctionInfo::ScriptIterator iterator(isolate, old_infos); 961 while (SharedFunctionInfo* shared = iterator.Next()) { 962 // We can't use SharedFunctionInfo::SetScript(info, undefined_value()) here, 963 // as we severed the link from the Script to the SharedFunctionInfo above. 964 Handle<SharedFunctionInfo> info(shared, isolate); 965 info->set_script(isolate->heap()->undefined_value()); 966 Handle<Object> new_noscript_list = WeakFixedArray::Add( 967 isolate->factory()->noscript_shared_function_infos(), info); 968 isolate->heap()->SetRootNoScriptSharedFunctionInfos(*new_noscript_list); 969 970 // Put the SharedFunctionInfo at its new, correct location. 971 SharedFunctionInfo::SetScript(info, script); 972 } 973 } 974 975 void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper, 976 Handle<Object> script_handle) { 977 Handle<SharedFunctionInfo> shared_info = 978 UnwrapSharedFunctionInfoFromJSValue(function_wrapper); 979 Isolate* isolate = function_wrapper->GetIsolate(); 980 CHECK(script_handle->IsScript() || script_handle->IsUndefined(isolate)); 981 SharedFunctionInfo::SetScript(shared_info, script_handle); 982 shared_info->DisableOptimization(kLiveEdit); 983 984 function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info); 985 } 986 987 namespace { 988 // For a script text change (defined as position_change_array), translates 989 // position in unchanged text to position in changed text. 990 // Text change is a set of non-overlapping regions in text, that have changed 991 // their contents and length. It is specified as array of groups of 3 numbers: 992 // (change_begin, change_end, change_end_new_position). 993 // Each group describes a change in text; groups are sorted by change_begin. 994 // Only position in text beyond any changes may be successfully translated. 995 // If a positions is inside some region that changed, result is currently 996 // undefined. 997 static int TranslatePosition(int original_position, 998 Handle<JSArray> position_change_array) { 999 int position_diff = 0; 1000 int array_len = GetArrayLength(position_change_array); 1001 Isolate* isolate = position_change_array->GetIsolate(); 1002 // TODO(635): binary search may be used here 1003 for (int i = 0; i < array_len; i += 3) { 1004 HandleScope scope(isolate); 1005 Handle<Object> element = 1006 JSReceiver::GetElement(isolate, position_change_array, i) 1007 .ToHandleChecked(); 1008 CHECK(element->IsSmi()); 1009 int chunk_start = Handle<Smi>::cast(element)->value(); 1010 if (original_position < chunk_start) { 1011 break; 1012 } 1013 element = JSReceiver::GetElement(isolate, position_change_array, i + 1) 1014 .ToHandleChecked(); 1015 CHECK(element->IsSmi()); 1016 int chunk_end = Handle<Smi>::cast(element)->value(); 1017 // Position mustn't be inside a chunk. 1018 DCHECK(original_position >= chunk_end); 1019 element = JSReceiver::GetElement(isolate, position_change_array, i + 2) 1020 .ToHandleChecked(); 1021 CHECK(element->IsSmi()); 1022 int chunk_changed_end = Handle<Smi>::cast(element)->value(); 1023 position_diff = chunk_changed_end - chunk_end; 1024 } 1025 1026 return original_position + position_diff; 1027 } 1028 1029 void TranslateSourcePositionTable(Handle<AbstractCode> code, 1030 Handle<JSArray> position_change_array) { 1031 Isolate* isolate = code->GetIsolate(); 1032 Zone zone(isolate->allocator(), ZONE_NAME); 1033 SourcePositionTableBuilder builder(&zone); 1034 1035 Handle<ByteArray> source_position_table(code->source_position_table()); 1036 for (SourcePositionTableIterator iterator(*source_position_table); 1037 !iterator.done(); iterator.Advance()) { 1038 SourcePosition position = iterator.source_position(); 1039 position.SetScriptOffset( 1040 TranslatePosition(position.ScriptOffset(), position_change_array)); 1041 builder.AddPosition(iterator.code_offset(), position, 1042 iterator.is_statement()); 1043 } 1044 1045 Handle<ByteArray> new_source_position_table( 1046 builder.ToSourcePositionTable(isolate, code)); 1047 code->set_source_position_table(*new_source_position_table); 1048 } 1049 } // namespace 1050 1051 void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array, 1052 Handle<JSArray> position_change_array) { 1053 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1054 Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo(); 1055 1056 int old_function_start = info->start_position(); 1057 int new_function_start = TranslatePosition(old_function_start, 1058 position_change_array); 1059 int new_function_end = TranslatePosition(info->end_position(), 1060 position_change_array); 1061 int new_function_token_pos = 1062 TranslatePosition(info->function_token_position(), position_change_array); 1063 1064 info->set_start_position(new_function_start); 1065 info->set_end_position(new_function_end); 1066 info->set_function_token_position(new_function_token_pos); 1067 1068 if (info->HasBytecodeArray()) { 1069 TranslateSourcePositionTable( 1070 Handle<AbstractCode>(AbstractCode::cast(info->bytecode_array())), 1071 position_change_array); 1072 } 1073 if (info->code()->kind() == Code::FUNCTION) { 1074 TranslateSourcePositionTable( 1075 Handle<AbstractCode>(AbstractCode::cast(info->code())), 1076 position_change_array); 1077 } 1078 if (info->HasDebugInfo()) { 1079 // Existing break points will be re-applied. Reset the debug info here. 1080 info->GetIsolate()->debug()->RemoveDebugInfoAndClearFromShared( 1081 handle(info->GetDebugInfo())); 1082 } 1083 } 1084 1085 1086 static Handle<Script> CreateScriptCopy(Handle<Script> original) { 1087 Isolate* isolate = original->GetIsolate(); 1088 1089 Handle<String> original_source(String::cast(original->source())); 1090 Handle<Script> copy = isolate->factory()->NewScript(original_source); 1091 1092 copy->set_name(original->name()); 1093 copy->set_line_offset(original->line_offset()); 1094 copy->set_column_offset(original->column_offset()); 1095 copy->set_type(original->type()); 1096 copy->set_context_data(original->context_data()); 1097 copy->set_eval_from_shared(original->eval_from_shared()); 1098 copy->set_eval_from_position(original->eval_from_position()); 1099 1100 Handle<FixedArray> infos(isolate->factory()->NewFixedArray( 1101 original->shared_function_infos()->length())); 1102 copy->set_shared_function_infos(*infos); 1103 1104 // Copy all the flags, but clear compilation state. 1105 copy->set_flags(original->flags()); 1106 copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL); 1107 1108 return copy; 1109 } 1110 1111 Handle<Object> LiveEdit::ChangeScriptSource(Handle<Script> original_script, 1112 Handle<String> new_source, 1113 Handle<Object> old_script_name) { 1114 Isolate* isolate = original_script->GetIsolate(); 1115 Handle<Object> old_script_object; 1116 if (old_script_name->IsString()) { 1117 Handle<Script> old_script = CreateScriptCopy(original_script); 1118 old_script->set_name(String::cast(*old_script_name)); 1119 old_script_object = old_script; 1120 isolate->debug()->OnAfterCompile(old_script); 1121 } else { 1122 old_script_object = isolate->factory()->null_value(); 1123 } 1124 1125 original_script->set_source(*new_source); 1126 1127 // Drop line ends so that they will be recalculated. 1128 original_script->set_line_ends(isolate->heap()->undefined_value()); 1129 1130 return old_script_object; 1131 } 1132 1133 1134 1135 void LiveEdit::ReplaceRefToNestedFunction( 1136 Handle<JSValue> parent_function_wrapper, 1137 Handle<JSValue> orig_function_wrapper, 1138 Handle<JSValue> subst_function_wrapper) { 1139 1140 Handle<SharedFunctionInfo> parent_shared = 1141 UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper); 1142 Handle<SharedFunctionInfo> orig_shared = 1143 UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper); 1144 Handle<SharedFunctionInfo> subst_shared = 1145 UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper); 1146 1147 for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) { 1148 if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) { 1149 if (it.rinfo()->target_object() == *orig_shared) { 1150 it.rinfo()->set_target_object(*subst_shared); 1151 } 1152 } 1153 } 1154 } 1155 1156 1157 // Check an activation against list of functions. If there is a function 1158 // that matches, its status in result array is changed to status argument value. 1159 static bool CheckActivation(Handle<JSArray> shared_info_array, 1160 Handle<JSArray> result, 1161 StackFrame* frame, 1162 LiveEdit::FunctionPatchabilityStatus status) { 1163 if (!frame->is_java_script()) return false; 1164 1165 Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function()); 1166 1167 Isolate* isolate = shared_info_array->GetIsolate(); 1168 int len = GetArrayLength(shared_info_array); 1169 for (int i = 0; i < len; i++) { 1170 HandleScope scope(isolate); 1171 Handle<Object> element = 1172 JSReceiver::GetElement(isolate, shared_info_array, i).ToHandleChecked(); 1173 Handle<JSValue> jsvalue = Handle<JSValue>::cast(element); 1174 Handle<SharedFunctionInfo> shared = 1175 UnwrapSharedFunctionInfoFromJSValue(jsvalue); 1176 1177 if (function->Inlines(*shared)) { 1178 SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate)); 1179 return true; 1180 } 1181 } 1182 return false; 1183 } 1184 1185 // Describes a set of call frames that execute any of listed functions. 1186 // Finding no such frames does not mean error. 1187 class MultipleFunctionTarget { 1188 public: 1189 MultipleFunctionTarget(Handle<JSArray> old_shared_array, 1190 Handle<JSArray> new_shared_array, 1191 Handle<JSArray> result) 1192 : old_shared_array_(old_shared_array), 1193 new_shared_array_(new_shared_array), 1194 result_(result) {} 1195 bool MatchActivation(StackFrame* frame, 1196 LiveEdit::FunctionPatchabilityStatus status) { 1197 return CheckActivation(old_shared_array_, result_, frame, status); 1198 } 1199 const char* GetNotFoundMessage() const { 1200 return NULL; 1201 } 1202 bool FrameUsesNewTarget(StackFrame* frame) { 1203 if (!frame->is_java_script()) return false; 1204 JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame); 1205 Handle<SharedFunctionInfo> old_shared(jsframe->function()->shared()); 1206 Isolate* isolate = old_shared->GetIsolate(); 1207 int len = GetArrayLength(old_shared_array_); 1208 // Find corresponding new shared function info and return whether it 1209 // references new.target. 1210 for (int i = 0; i < len; i++) { 1211 HandleScope scope(isolate); 1212 Handle<Object> old_element = 1213 JSReceiver::GetElement(isolate, old_shared_array_, i) 1214 .ToHandleChecked(); 1215 if (!old_shared.is_identical_to(UnwrapSharedFunctionInfoFromJSValue( 1216 Handle<JSValue>::cast(old_element)))) { 1217 continue; 1218 } 1219 1220 Handle<Object> new_element = 1221 JSReceiver::GetElement(isolate, new_shared_array_, i) 1222 .ToHandleChecked(); 1223 if (new_element->IsUndefined(isolate)) return false; 1224 Handle<SharedFunctionInfo> new_shared = 1225 UnwrapSharedFunctionInfoFromJSValue( 1226 Handle<JSValue>::cast(new_element)); 1227 if (new_shared->scope_info()->HasNewTarget()) { 1228 SetElementSloppy( 1229 result_, i, 1230 Handle<Smi>( 1231 Smi::FromInt( 1232 LiveEdit::FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART), 1233 isolate)); 1234 return true; 1235 } 1236 return false; 1237 } 1238 return false; 1239 } 1240 1241 void set_status(LiveEdit::FunctionPatchabilityStatus status) { 1242 Isolate* isolate = old_shared_array_->GetIsolate(); 1243 int len = GetArrayLength(old_shared_array_); 1244 for (int i = 0; i < len; ++i) { 1245 Handle<Object> old_element = 1246 JSReceiver::GetElement(isolate, result_, i).ToHandleChecked(); 1247 if (!old_element->IsSmi() || 1248 Smi::cast(*old_element)->value() == 1249 LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) { 1250 SetElementSloppy(result_, i, 1251 Handle<Smi>(Smi::FromInt(status), isolate)); 1252 } 1253 } 1254 } 1255 1256 private: 1257 Handle<JSArray> old_shared_array_; 1258 Handle<JSArray> new_shared_array_; 1259 Handle<JSArray> result_; 1260 }; 1261 1262 1263 // Drops all call frame matched by target and all frames above them. 1264 template <typename TARGET> 1265 static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, 1266 TARGET& target, // NOLINT 1267 bool do_drop) { 1268 Debug* debug = isolate->debug(); 1269 Zone zone(isolate->allocator(), ZONE_NAME); 1270 Vector<StackFrame*> frames = CreateStackMap(isolate, &zone); 1271 1272 int top_frame_index = -1; 1273 int frame_index = 0; 1274 for (; frame_index < frames.length(); frame_index++) { 1275 StackFrame* frame = frames[frame_index]; 1276 if (frame->id() == debug->break_frame_id()) { 1277 top_frame_index = frame_index; 1278 break; 1279 } 1280 if (target.MatchActivation( 1281 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { 1282 // We are still above break_frame. It is not a target frame, 1283 // it is a problem. 1284 return "Debugger mark-up on stack is not found"; 1285 } 1286 } 1287 1288 if (top_frame_index == -1) { 1289 // We haven't found break frame, but no function is blocking us anyway. 1290 return target.GetNotFoundMessage(); 1291 } 1292 1293 bool target_frame_found = false; 1294 int bottom_js_frame_index = top_frame_index; 1295 bool non_droppable_frame_found = false; 1296 LiveEdit::FunctionPatchabilityStatus non_droppable_reason; 1297 1298 for (; frame_index < frames.length(); frame_index++) { 1299 StackFrame* frame = frames[frame_index]; 1300 if (frame->is_exit() || frame->is_builtin_exit()) { 1301 non_droppable_frame_found = true; 1302 non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE; 1303 break; 1304 } 1305 if (frame->is_java_script()) { 1306 SharedFunctionInfo* shared = 1307 JavaScriptFrame::cast(frame)->function()->shared(); 1308 if (IsResumableFunction(shared->kind())) { 1309 non_droppable_frame_found = true; 1310 non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR; 1311 break; 1312 } 1313 } 1314 if (target.MatchActivation( 1315 frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { 1316 target_frame_found = true; 1317 bottom_js_frame_index = frame_index; 1318 } 1319 } 1320 1321 if (non_droppable_frame_found) { 1322 // There is a C or generator frame on stack. We can't drop C frames, and we 1323 // can't restart generators. Check that there are no target frames below 1324 // them. 1325 for (; frame_index < frames.length(); frame_index++) { 1326 StackFrame* frame = frames[frame_index]; 1327 if (frame->is_java_script()) { 1328 if (target.MatchActivation(frame, non_droppable_reason)) { 1329 // Fail. 1330 return NULL; 1331 } 1332 if (non_droppable_reason == 1333 LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR && 1334 !target_frame_found) { 1335 // Fail. 1336 target.set_status(non_droppable_reason); 1337 return NULL; 1338 } 1339 } 1340 } 1341 } 1342 1343 // We cannot restart a frame that uses new.target. 1344 if (target.FrameUsesNewTarget(frames[bottom_js_frame_index])) return NULL; 1345 1346 if (!do_drop) { 1347 // We are in check-only mode. 1348 return NULL; 1349 } 1350 1351 if (!target_frame_found) { 1352 // Nothing to drop. 1353 return target.GetNotFoundMessage(); 1354 } 1355 1356 if (!LiveEdit::kFrameDropperSupported) { 1357 return "Stack manipulations are not supported in this architecture."; 1358 } 1359 1360 debug->ScheduleFrameRestart(frames[bottom_js_frame_index]); 1361 return NULL; 1362 } 1363 1364 1365 // Fills result array with statuses of functions. Modifies the stack 1366 // removing all listed function if possible and if do_drop is true. 1367 static const char* DropActivationsInActiveThread( 1368 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array, 1369 Handle<JSArray> result, bool do_drop) { 1370 MultipleFunctionTarget target(old_shared_array, new_shared_array, result); 1371 Isolate* isolate = old_shared_array->GetIsolate(); 1372 1373 const char* message = 1374 DropActivationsInActiveThreadImpl(isolate, target, do_drop); 1375 if (message) { 1376 return message; 1377 } 1378 1379 int array_len = GetArrayLength(old_shared_array); 1380 1381 // Replace "blocked on active" with "replaced on active" status. 1382 for (int i = 0; i < array_len; i++) { 1383 Handle<Object> obj = 1384 JSReceiver::GetElement(isolate, result, i).ToHandleChecked(); 1385 if (*obj == Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { 1386 Handle<Object> replaced( 1387 Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate); 1388 SetElementSloppy(result, i, replaced); 1389 } 1390 } 1391 return NULL; 1392 } 1393 1394 1395 bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array, 1396 Handle<FixedArray> result, 1397 int len) { 1398 Isolate* isolate = shared_info_array->GetIsolate(); 1399 bool found_suspended_activations = false; 1400 1401 DCHECK_LE(len, result->length()); 1402 1403 FunctionPatchabilityStatus active = FUNCTION_BLOCKED_ACTIVE_GENERATOR; 1404 1405 Heap* heap = isolate->heap(); 1406 HeapIterator iterator(heap, HeapIterator::kFilterUnreachable); 1407 HeapObject* obj = NULL; 1408 while ((obj = iterator.next()) != NULL) { 1409 if (!obj->IsJSGeneratorObject()) continue; 1410 1411 JSGeneratorObject* gen = JSGeneratorObject::cast(obj); 1412 if (gen->is_closed()) continue; 1413 1414 HandleScope scope(isolate); 1415 1416 for (int i = 0; i < len; i++) { 1417 Handle<JSValue> jsvalue = Handle<JSValue>::cast( 1418 FixedArray::get(*shared_info_array, i, isolate)); 1419 Handle<SharedFunctionInfo> shared = 1420 UnwrapSharedFunctionInfoFromJSValue(jsvalue); 1421 1422 if (gen->function()->shared() == *shared) { 1423 result->set(i, Smi::FromInt(active)); 1424 found_suspended_activations = true; 1425 } 1426 } 1427 } 1428 1429 return found_suspended_activations; 1430 } 1431 1432 1433 class InactiveThreadActivationsChecker : public ThreadVisitor { 1434 public: 1435 InactiveThreadActivationsChecker(Handle<JSArray> old_shared_array, 1436 Handle<JSArray> result) 1437 : old_shared_array_(old_shared_array), 1438 result_(result), 1439 has_blocked_functions_(false) {} 1440 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { 1441 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { 1442 has_blocked_functions_ |= 1443 CheckActivation(old_shared_array_, result_, it.frame(), 1444 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK); 1445 } 1446 } 1447 bool HasBlockedFunctions() { 1448 return has_blocked_functions_; 1449 } 1450 1451 private: 1452 Handle<JSArray> old_shared_array_; 1453 Handle<JSArray> result_; 1454 bool has_blocked_functions_; 1455 }; 1456 1457 1458 Handle<JSArray> LiveEdit::CheckAndDropActivations( 1459 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array, 1460 bool do_drop) { 1461 Isolate* isolate = old_shared_array->GetIsolate(); 1462 int len = GetArrayLength(old_shared_array); 1463 1464 DCHECK(old_shared_array->HasFastElements()); 1465 Handle<FixedArray> old_shared_array_elements( 1466 FixedArray::cast(old_shared_array->elements())); 1467 1468 Handle<JSArray> result = isolate->factory()->NewJSArray(len); 1469 result->set_length(Smi::FromInt(len)); 1470 JSObject::EnsureWritableFastElements(result); 1471 Handle<FixedArray> result_elements = 1472 handle(FixedArray::cast(result->elements()), isolate); 1473 1474 // Fill the default values. 1475 for (int i = 0; i < len; i++) { 1476 FunctionPatchabilityStatus status = FUNCTION_AVAILABLE_FOR_PATCH; 1477 result_elements->set(i, Smi::FromInt(status)); 1478 } 1479 1480 // Scan the heap for active generators -- those that are either currently 1481 // running (as we wouldn't want to restart them, because we don't know where 1482 // to restart them from) or suspended. Fail if any one corresponds to the set 1483 // of functions being edited. 1484 if (FindActiveGenerators(old_shared_array_elements, result_elements, len)) { 1485 return result; 1486 } 1487 1488 // Check inactive threads. Fail if some functions are blocked there. 1489 InactiveThreadActivationsChecker inactive_threads_checker(old_shared_array, 1490 result); 1491 isolate->thread_manager()->IterateArchivedThreads( 1492 &inactive_threads_checker); 1493 if (inactive_threads_checker.HasBlockedFunctions()) { 1494 return result; 1495 } 1496 1497 // Try to drop activations from the current stack. 1498 const char* error_message = DropActivationsInActiveThread( 1499 old_shared_array, new_shared_array, result, do_drop); 1500 if (error_message != NULL) { 1501 // Add error message as an array extra element. 1502 Handle<String> str = 1503 isolate->factory()->NewStringFromAsciiChecked(error_message); 1504 SetElementSloppy(result, len, str); 1505 } 1506 return result; 1507 } 1508 1509 1510 // Describes a single callframe a target. Not finding this frame 1511 // means an error. 1512 class SingleFrameTarget { 1513 public: 1514 explicit SingleFrameTarget(JavaScriptFrame* frame) 1515 : m_frame(frame), 1516 m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {} 1517 1518 bool MatchActivation(StackFrame* frame, 1519 LiveEdit::FunctionPatchabilityStatus status) { 1520 if (frame->fp() == m_frame->fp()) { 1521 m_saved_status = status; 1522 return true; 1523 } 1524 return false; 1525 } 1526 const char* GetNotFoundMessage() const { 1527 return "Failed to found requested frame"; 1528 } 1529 LiveEdit::FunctionPatchabilityStatus saved_status() { 1530 return m_saved_status; 1531 } 1532 void set_status(LiveEdit::FunctionPatchabilityStatus status) { 1533 m_saved_status = status; 1534 } 1535 1536 bool FrameUsesNewTarget(StackFrame* frame) { 1537 if (!frame->is_java_script()) return false; 1538 JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame); 1539 Handle<SharedFunctionInfo> shared(jsframe->function()->shared()); 1540 return shared->scope_info()->HasNewTarget(); 1541 } 1542 1543 private: 1544 JavaScriptFrame* m_frame; 1545 LiveEdit::FunctionPatchabilityStatus m_saved_status; 1546 }; 1547 1548 1549 // Finds a drops required frame and all frames above. 1550 // Returns error message or NULL. 1551 const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) { 1552 SingleFrameTarget target(frame); 1553 1554 const char* result = 1555 DropActivationsInActiveThreadImpl(frame->isolate(), target, true); 1556 if (result != NULL) { 1557 return result; 1558 } 1559 if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) { 1560 return "Function is blocked under native code"; 1561 } 1562 if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR) { 1563 return "Function is blocked under a generator activation"; 1564 } 1565 return NULL; 1566 } 1567 1568 Handle<JSArray> LiveEditFunctionTracker::Collect(FunctionLiteral* node, 1569 Handle<Script> script, 1570 Zone* zone, Isolate* isolate) { 1571 LiveEditFunctionTracker visitor(script, zone, isolate); 1572 visitor.VisitFunctionLiteral(node); 1573 return visitor.result_; 1574 } 1575 1576 LiveEditFunctionTracker::LiveEditFunctionTracker(Handle<Script> script, 1577 Zone* zone, Isolate* isolate) 1578 : AstTraversalVisitor<LiveEditFunctionTracker>(isolate) { 1579 current_parent_index_ = -1; 1580 isolate_ = isolate; 1581 len_ = 0; 1582 result_ = isolate->factory()->NewJSArray(10); 1583 script_ = script; 1584 zone_ = zone; 1585 } 1586 1587 void LiveEditFunctionTracker::VisitFunctionLiteral(FunctionLiteral* node) { 1588 // FunctionStarted is called in pre-order. 1589 FunctionStarted(node); 1590 // Recurse using the regular traversal. 1591 AstTraversalVisitor::VisitFunctionLiteral(node); 1592 // FunctionDone are called in post-order. 1593 Handle<SharedFunctionInfo> info = 1594 script_->FindSharedFunctionInfo(isolate_, node).ToHandleChecked(); 1595 FunctionDone(info, node->scope()); 1596 } 1597 1598 void LiveEditFunctionTracker::FunctionStarted(FunctionLiteral* fun) { 1599 HandleScope handle_scope(isolate_); 1600 FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate_); 1601 info.SetInitialProperties(fun->name(), fun->start_position(), 1602 fun->end_position(), fun->parameter_count(), 1603 current_parent_index_, fun->function_literal_id()); 1604 current_parent_index_ = len_; 1605 SetElementSloppy(result_, len_, info.GetJSArray()); 1606 len_++; 1607 } 1608 1609 // Saves full information about a function: its code, its scope info 1610 // and a SharedFunctionInfo object. 1611 void LiveEditFunctionTracker::FunctionDone(Handle<SharedFunctionInfo> shared, 1612 Scope* scope) { 1613 HandleScope handle_scope(isolate_); 1614 FunctionInfoWrapper info = FunctionInfoWrapper::cast( 1615 *JSReceiver::GetElement(isolate_, result_, current_parent_index_) 1616 .ToHandleChecked()); 1617 info.SetSharedFunctionInfo(shared); 1618 1619 Handle<Object> scope_info_list = SerializeFunctionScope(scope); 1620 info.SetFunctionScopeInfo(scope_info_list); 1621 1622 current_parent_index_ = info.GetParentIndex(); 1623 } 1624 1625 Handle<Object> LiveEditFunctionTracker::SerializeFunctionScope(Scope* scope) { 1626 Handle<JSArray> scope_info_list = isolate_->factory()->NewJSArray(10); 1627 int scope_info_length = 0; 1628 1629 // Saves some description of scope. It stores name and indexes of 1630 // variables in the whole scope chain. Null-named slots delimit 1631 // scopes of this chain. 1632 Scope* current_scope = scope; 1633 while (current_scope != NULL) { 1634 HandleScope handle_scope(isolate_); 1635 for (Variable* var : *current_scope->locals()) { 1636 if (!var->IsContextSlot()) continue; 1637 int context_index = var->index() - Context::MIN_CONTEXT_SLOTS; 1638 int location = scope_info_length + context_index * 2; 1639 SetElementSloppy(scope_info_list, location, var->name()); 1640 SetElementSloppy(scope_info_list, location + 1, 1641 handle(Smi::FromInt(var->index()), isolate_)); 1642 } 1643 scope_info_length += current_scope->ContextLocalCount() * 2; 1644 SetElementSloppy(scope_info_list, scope_info_length, 1645 isolate_->factory()->null_value()); 1646 scope_info_length++; 1647 1648 current_scope = current_scope->outer_scope(); 1649 } 1650 1651 return scope_info_list; 1652 } 1653 1654 } // namespace internal 1655 } // namespace v8 1656