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