1 // Copyright 2011 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 "compilation-cache.h" 34 #include "compiler.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 JSObject::SetElement(object, index, value, NONE, 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(Handle<String> s1, int pos1, 272 Handle<String> s2, int pos2, int len) { 273 for (int i = 0; i < len; i++) { 274 if (s1->Get(i + pos1) != s2->Get(i + pos2)) { 275 return false; 276 } 277 } 278 return true; 279 } 280 281 282 // Additional to Input interface. Lets switch Input range to subrange. 283 // More elegant way would be to wrap one Input as another Input object 284 // and translate positions there, but that would cost us additional virtual 285 // call per comparison. 286 class SubrangableInput : public Comparator::Input { 287 public: 288 virtual void SetSubrange1(int offset, int len) = 0; 289 virtual void SetSubrange2(int offset, int len) = 0; 290 }; 291 292 293 class SubrangableOutput : public Comparator::Output { 294 public: 295 virtual void SetSubrange1(int offset, int len) = 0; 296 virtual void SetSubrange2(int offset, int len) = 0; 297 }; 298 299 300 static int min(int a, int b) { 301 return a < b ? a : b; 302 } 303 304 305 // Finds common prefix and suffix in input. This parts shouldn't take space in 306 // linear programming table. Enable subranging in input and output. 307 static void NarrowDownInput(SubrangableInput* input, 308 SubrangableOutput* output) { 309 const int len1 = input->GetLength1(); 310 const int len2 = input->GetLength2(); 311 312 int common_prefix_len; 313 int common_suffix_len; 314 315 { 316 common_prefix_len = 0; 317 int prefix_limit = min(len1, len2); 318 while (common_prefix_len < prefix_limit && 319 input->Equals(common_prefix_len, common_prefix_len)) { 320 common_prefix_len++; 321 } 322 323 common_suffix_len = 0; 324 int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len); 325 326 while (common_suffix_len < suffix_limit && 327 input->Equals(len1 - common_suffix_len - 1, 328 len2 - common_suffix_len - 1)) { 329 common_suffix_len++; 330 } 331 } 332 333 if (common_prefix_len > 0 || common_suffix_len > 0) { 334 int new_len1 = len1 - common_suffix_len - common_prefix_len; 335 int new_len2 = len2 - common_suffix_len - common_prefix_len; 336 337 input->SetSubrange1(common_prefix_len, new_len1); 338 input->SetSubrange2(common_prefix_len, new_len2); 339 340 output->SetSubrange1(common_prefix_len, new_len1); 341 output->SetSubrange2(common_prefix_len, new_len2); 342 } 343 } 344 345 346 // A helper class that writes chunk numbers into JSArray. 347 // Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end). 348 class CompareOutputArrayWriter { 349 public: 350 CompareOutputArrayWriter() 351 : array_(FACTORY->NewJSArray(10)), current_size_(0) {} 352 353 Handle<JSArray> GetResult() { 354 return array_; 355 } 356 357 void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) { 358 SetElementNonStrict(array_, 359 current_size_, 360 Handle<Object>(Smi::FromInt(char_pos1))); 361 SetElementNonStrict(array_, 362 current_size_ + 1, 363 Handle<Object>(Smi::FromInt(char_pos1 + char_len1))); 364 SetElementNonStrict(array_, 365 current_size_ + 2, 366 Handle<Object>(Smi::FromInt(char_pos2 + char_len2))); 367 current_size_ += 3; 368 } 369 370 private: 371 Handle<JSArray> array_; 372 int current_size_; 373 }; 374 375 376 // Represents 2 strings as 2 arrays of tokens. 377 // TODO(LiveEdit): Currently it's actually an array of charactres. 378 // Make array of tokens instead. 379 class TokensCompareInput : public Comparator::Input { 380 public: 381 TokensCompareInput(Handle<String> s1, int offset1, int len1, 382 Handle<String> s2, int offset2, int len2) 383 : s1_(s1), offset1_(offset1), len1_(len1), 384 s2_(s2), offset2_(offset2), len2_(len2) { 385 } 386 virtual int GetLength1() { 387 return len1_; 388 } 389 virtual int GetLength2() { 390 return len2_; 391 } 392 bool Equals(int index1, int index2) { 393 return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2); 394 } 395 396 private: 397 Handle<String> s1_; 398 int offset1_; 399 int len1_; 400 Handle<String> s2_; 401 int offset2_; 402 int len2_; 403 }; 404 405 406 // Stores compare result in JSArray. Converts substring positions 407 // to absolute positions. 408 class TokensCompareOutput : public Comparator::Output { 409 public: 410 TokensCompareOutput(CompareOutputArrayWriter* array_writer, 411 int offset1, int offset2) 412 : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) { 413 } 414 415 void AddChunk(int pos1, int pos2, int len1, int len2) { 416 array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2); 417 } 418 419 private: 420 CompareOutputArrayWriter* array_writer_; 421 int offset1_; 422 int offset2_; 423 }; 424 425 426 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line 427 // never has terminating new line character. 428 class LineEndsWrapper { 429 public: 430 explicit LineEndsWrapper(Handle<String> string) 431 : ends_array_(CalculateLineEnds(string, false)), 432 string_len_(string->length()) { 433 } 434 int length() { 435 return ends_array_->length() + 1; 436 } 437 // Returns start for any line including start of the imaginary line after 438 // the last line. 439 int GetLineStart(int index) { 440 if (index == 0) { 441 return 0; 442 } else { 443 return GetLineEnd(index - 1); 444 } 445 } 446 int GetLineEnd(int index) { 447 if (index == ends_array_->length()) { 448 // End of the last line is always an end of the whole string. 449 // If the string ends with a new line character, the last line is an 450 // empty string after this character. 451 return string_len_; 452 } else { 453 return GetPosAfterNewLine(index); 454 } 455 } 456 457 private: 458 Handle<FixedArray> ends_array_; 459 int string_len_; 460 461 int GetPosAfterNewLine(int index) { 462 return Smi::cast(ends_array_->get(index))->value() + 1; 463 } 464 }; 465 466 467 // Represents 2 strings as 2 arrays of lines. 468 class LineArrayCompareInput : public SubrangableInput { 469 public: 470 LineArrayCompareInput(Handle<String> s1, Handle<String> s2, 471 LineEndsWrapper line_ends1, LineEndsWrapper line_ends2) 472 : s1_(s1), s2_(s2), line_ends1_(line_ends1), 473 line_ends2_(line_ends2), 474 subrange_offset1_(0), subrange_offset2_(0), 475 subrange_len1_(line_ends1_.length()), 476 subrange_len2_(line_ends2_.length()) { 477 } 478 int GetLength1() { 479 return subrange_len1_; 480 } 481 int GetLength2() { 482 return subrange_len2_; 483 } 484 bool Equals(int index1, int index2) { 485 index1 += subrange_offset1_; 486 index2 += subrange_offset2_; 487 488 int line_start1 = line_ends1_.GetLineStart(index1); 489 int line_start2 = line_ends2_.GetLineStart(index2); 490 int line_end1 = line_ends1_.GetLineEnd(index1); 491 int line_end2 = line_ends2_.GetLineEnd(index2); 492 int len1 = line_end1 - line_start1; 493 int len2 = line_end2 - line_start2; 494 if (len1 != len2) { 495 return false; 496 } 497 return CompareSubstrings(s1_, line_start1, s2_, line_start2, 498 len1); 499 } 500 void SetSubrange1(int offset, int len) { 501 subrange_offset1_ = offset; 502 subrange_len1_ = len; 503 } 504 void SetSubrange2(int offset, int len) { 505 subrange_offset2_ = offset; 506 subrange_len2_ = len; 507 } 508 509 private: 510 Handle<String> s1_; 511 Handle<String> s2_; 512 LineEndsWrapper line_ends1_; 513 LineEndsWrapper line_ends2_; 514 int subrange_offset1_; 515 int subrange_offset2_; 516 int subrange_len1_; 517 int subrange_len2_; 518 }; 519 520 521 // Stores compare result in JSArray. For each chunk tries to conduct 522 // a fine-grained nested diff token-wise. 523 class TokenizingLineArrayCompareOutput : public SubrangableOutput { 524 public: 525 TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1, 526 LineEndsWrapper line_ends2, 527 Handle<String> s1, Handle<String> s2) 528 : line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2), 529 subrange_offset1_(0), subrange_offset2_(0) { 530 } 531 532 void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) { 533 line_pos1 += subrange_offset1_; 534 line_pos2 += subrange_offset2_; 535 536 int char_pos1 = line_ends1_.GetLineStart(line_pos1); 537 int char_pos2 = line_ends2_.GetLineStart(line_pos2); 538 int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1; 539 int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2; 540 541 if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) { 542 // Chunk is small enough to conduct a nested token-level diff. 543 HandleScope subTaskScope; 544 545 TokensCompareInput tokens_input(s1_, char_pos1, char_len1, 546 s2_, char_pos2, char_len2); 547 TokensCompareOutput tokens_output(&array_writer_, char_pos1, 548 char_pos2); 549 550 Comparator::CalculateDifference(&tokens_input, &tokens_output); 551 } else { 552 array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2); 553 } 554 } 555 void SetSubrange1(int offset, int len) { 556 subrange_offset1_ = offset; 557 } 558 void SetSubrange2(int offset, int len) { 559 subrange_offset2_ = offset; 560 } 561 562 Handle<JSArray> GetResult() { 563 return array_writer_.GetResult(); 564 } 565 566 private: 567 static const int CHUNK_LEN_LIMIT = 800; 568 569 CompareOutputArrayWriter array_writer_; 570 LineEndsWrapper line_ends1_; 571 LineEndsWrapper line_ends2_; 572 Handle<String> s1_; 573 Handle<String> s2_; 574 int subrange_offset1_; 575 int subrange_offset2_; 576 }; 577 578 579 Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1, 580 Handle<String> s2) { 581 s1 = FlattenGetString(s1); 582 s2 = FlattenGetString(s2); 583 584 LineEndsWrapper line_ends1(s1); 585 LineEndsWrapper line_ends2(s2); 586 587 LineArrayCompareInput input(s1, s2, line_ends1, line_ends2); 588 TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2); 589 590 NarrowDownInput(&input, &output); 591 592 Comparator::CalculateDifference(&input, &output); 593 594 return output.GetResult(); 595 } 596 597 598 static void CompileScriptForTracker(Isolate* isolate, Handle<Script> script) { 599 // TODO(635): support extensions. 600 PostponeInterruptsScope postpone(isolate); 601 602 // Build AST. 603 CompilationInfo info(script); 604 info.MarkAsGlobal(); 605 // Parse and don't allow skipping lazy functions. 606 if (ParserApi::Parse(&info, kNoParsingFlags)) { 607 // Compile the code. 608 LiveEditFunctionTracker tracker(info.isolate(), info.function()); 609 if (Compiler::MakeCodeForLiveEdit(&info)) { 610 ASSERT(!info.code().is_null()); 611 tracker.RecordRootFunctionInfo(info.code()); 612 } else { 613 info.isolate()->StackOverflow(); 614 } 615 } 616 } 617 618 619 // Unwraps JSValue object, returning its field "value" 620 static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) { 621 return Handle<Object>(jsValue->value()); 622 } 623 624 625 // Wraps any object into a OpaqueReference, that will hide the object 626 // from JavaScript. 627 static Handle<JSValue> WrapInJSValue(Handle<Object> object) { 628 Handle<JSFunction> constructor = 629 Isolate::Current()->opaque_reference_function(); 630 Handle<JSValue> result = 631 Handle<JSValue>::cast(FACTORY->NewJSObject(constructor)); 632 result->set_value(*object); 633 return result; 634 } 635 636 637 // Simple helper class that creates more or less typed structures over 638 // JSArray object. This is an adhoc method of passing structures from C++ 639 // to JavaScript. 640 template<typename S> 641 class JSArrayBasedStruct { 642 public: 643 static S Create() { 644 Handle<JSArray> array = FACTORY->NewJSArray(S::kSize_); 645 return S(array); 646 } 647 static S cast(Object* object) { 648 JSArray* array = JSArray::cast(object); 649 Handle<JSArray> array_handle(array); 650 return S(array_handle); 651 } 652 explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) { 653 } 654 Handle<JSArray> GetJSArray() { 655 return array_; 656 } 657 658 protected: 659 void SetField(int field_position, Handle<Object> value) { 660 SetElementNonStrict(array_, field_position, value); 661 } 662 void SetSmiValueField(int field_position, int value) { 663 SetElementNonStrict(array_, 664 field_position, 665 Handle<Smi>(Smi::FromInt(value))); 666 } 667 Object* GetField(int field_position) { 668 return array_->GetElementNoExceptionThrown(field_position); 669 } 670 int GetSmiValueField(int field_position) { 671 Object* res = GetField(field_position); 672 return Smi::cast(res)->value(); 673 } 674 675 private: 676 Handle<JSArray> array_; 677 }; 678 679 680 // Represents some function compilation details. This structure will be used 681 // from JavaScript. It contains Code object, which is kept wrapped 682 // into a BlindReference for sanitizing reasons. 683 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> { 684 public: 685 explicit FunctionInfoWrapper(Handle<JSArray> array) 686 : JSArrayBasedStruct<FunctionInfoWrapper>(array) { 687 } 688 void SetInitialProperties(Handle<String> name, int start_position, 689 int end_position, int param_num, int parent_index) { 690 HandleScope scope; 691 this->SetField(kFunctionNameOffset_, name); 692 this->SetSmiValueField(kStartPositionOffset_, start_position); 693 this->SetSmiValueField(kEndPositionOffset_, end_position); 694 this->SetSmiValueField(kParamNumOffset_, param_num); 695 this->SetSmiValueField(kParentIndexOffset_, parent_index); 696 } 697 void SetFunctionCode(Handle<Code> function_code, 698 Handle<Object> code_scope_info) { 699 Handle<JSValue> code_wrapper = WrapInJSValue(function_code); 700 this->SetField(kCodeOffset_, code_wrapper); 701 702 Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info); 703 this->SetField(kCodeScopeInfoOffset_, scope_wrapper); 704 } 705 void SetOuterScopeInfo(Handle<Object> scope_info_array) { 706 this->SetField(kOuterScopeInfoOffset_, scope_info_array); 707 } 708 void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) { 709 Handle<JSValue> info_holder = WrapInJSValue(info); 710 this->SetField(kSharedFunctionInfoOffset_, info_holder); 711 } 712 int GetParentIndex() { 713 return this->GetSmiValueField(kParentIndexOffset_); 714 } 715 Handle<Code> GetFunctionCode() { 716 Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>( 717 JSValue::cast(this->GetField(kCodeOffset_)))); 718 return Handle<Code>::cast(raw_result); 719 } 720 Handle<Object> GetCodeScopeInfo() { 721 Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>( 722 JSValue::cast(this->GetField(kCodeScopeInfoOffset_)))); 723 return raw_result; 724 } 725 int GetStartPosition() { 726 return this->GetSmiValueField(kStartPositionOffset_); 727 } 728 int GetEndPosition() { 729 return this->GetSmiValueField(kEndPositionOffset_); 730 } 731 732 private: 733 static const int kFunctionNameOffset_ = 0; 734 static const int kStartPositionOffset_ = 1; 735 static const int kEndPositionOffset_ = 2; 736 static const int kParamNumOffset_ = 3; 737 static const int kCodeOffset_ = 4; 738 static const int kCodeScopeInfoOffset_ = 5; 739 static const int kOuterScopeInfoOffset_ = 6; 740 static const int kParentIndexOffset_ = 7; 741 static const int kSharedFunctionInfoOffset_ = 8; 742 static const int kSize_ = 9; 743 744 friend class JSArrayBasedStruct<FunctionInfoWrapper>; 745 }; 746 747 748 // Wraps SharedFunctionInfo along with some of its fields for passing it 749 // back to JavaScript. SharedFunctionInfo object itself is additionally 750 // wrapped into BlindReference for sanitizing reasons. 751 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> { 752 public: 753 static bool IsInstance(Handle<JSArray> array) { 754 return array->length() == Smi::FromInt(kSize_) && 755 array->GetElementNoExceptionThrown(kSharedInfoOffset_)->IsJSValue(); 756 } 757 758 explicit SharedInfoWrapper(Handle<JSArray> array) 759 : JSArrayBasedStruct<SharedInfoWrapper>(array) { 760 } 761 762 void SetProperties(Handle<String> name, int start_position, int end_position, 763 Handle<SharedFunctionInfo> info) { 764 HandleScope scope; 765 this->SetField(kFunctionNameOffset_, name); 766 Handle<JSValue> info_holder = WrapInJSValue(info); 767 this->SetField(kSharedInfoOffset_, info_holder); 768 this->SetSmiValueField(kStartPositionOffset_, start_position); 769 this->SetSmiValueField(kEndPositionOffset_, end_position); 770 } 771 Handle<SharedFunctionInfo> GetInfo() { 772 Object* element = this->GetField(kSharedInfoOffset_); 773 Handle<JSValue> value_wrapper(JSValue::cast(element)); 774 Handle<Object> raw_result = UnwrapJSValue(value_wrapper); 775 return Handle<SharedFunctionInfo>::cast(raw_result); 776 } 777 778 private: 779 static const int kFunctionNameOffset_ = 0; 780 static const int kStartPositionOffset_ = 1; 781 static const int kEndPositionOffset_ = 2; 782 static const int kSharedInfoOffset_ = 3; 783 static const int kSize_ = 4; 784 785 friend class JSArrayBasedStruct<SharedInfoWrapper>; 786 }; 787 788 789 class FunctionInfoListener { 790 public: 791 FunctionInfoListener() { 792 current_parent_index_ = -1; 793 len_ = 0; 794 result_ = FACTORY->NewJSArray(10); 795 } 796 797 void FunctionStarted(FunctionLiteral* fun) { 798 HandleScope scope; 799 FunctionInfoWrapper info = FunctionInfoWrapper::Create(); 800 info.SetInitialProperties(fun->name(), fun->start_position(), 801 fun->end_position(), fun->parameter_count(), 802 current_parent_index_); 803 current_parent_index_ = len_; 804 SetElementNonStrict(result_, len_, info.GetJSArray()); 805 len_++; 806 } 807 808 void FunctionDone() { 809 HandleScope scope; 810 FunctionInfoWrapper info = 811 FunctionInfoWrapper::cast( 812 result_->GetElementNoExceptionThrown(current_parent_index_)); 813 current_parent_index_ = info.GetParentIndex(); 814 } 815 816 // Saves only function code, because for a script function we 817 // may never create a SharedFunctionInfo object. 818 void FunctionCode(Handle<Code> function_code) { 819 FunctionInfoWrapper info = 820 FunctionInfoWrapper::cast( 821 result_->GetElementNoExceptionThrown(current_parent_index_)); 822 info.SetFunctionCode(function_code, Handle<Object>(HEAP->null_value())); 823 } 824 825 // Saves full information about a function: its code, its scope info 826 // and a SharedFunctionInfo object. 827 void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope) { 828 if (!shared->IsSharedFunctionInfo()) { 829 return; 830 } 831 FunctionInfoWrapper info = 832 FunctionInfoWrapper::cast( 833 result_->GetElementNoExceptionThrown(current_parent_index_)); 834 info.SetFunctionCode(Handle<Code>(shared->code()), 835 Handle<Object>(shared->scope_info())); 836 info.SetSharedFunctionInfo(shared); 837 838 Handle<Object> scope_info_list(SerializeFunctionScope(scope)); 839 info.SetOuterScopeInfo(scope_info_list); 840 } 841 842 Handle<JSArray> GetResult() { return result_; } 843 844 private: 845 Object* SerializeFunctionScope(Scope* scope) { 846 HandleScope handle_scope; 847 848 Handle<JSArray> scope_info_list = FACTORY->NewJSArray(10); 849 int scope_info_length = 0; 850 851 // Saves some description of scope. It stores name and indexes of 852 // variables in the whole scope chain. Null-named slots delimit 853 // scopes of this chain. 854 Scope* outer_scope = scope->outer_scope(); 855 if (outer_scope == NULL) { 856 return HEAP->undefined_value(); 857 } 858 do { 859 ZoneList<Variable*> stack_list(outer_scope->StackLocalCount()); 860 ZoneList<Variable*> context_list(outer_scope->ContextLocalCount()); 861 outer_scope->CollectStackAndContextLocals(&stack_list, &context_list); 862 context_list.Sort(&Variable::CompareIndex); 863 864 for (int i = 0; i < context_list.length(); i++) { 865 SetElementNonStrict(scope_info_list, 866 scope_info_length, 867 context_list[i]->name()); 868 scope_info_length++; 869 SetElementNonStrict( 870 scope_info_list, 871 scope_info_length, 872 Handle<Smi>(Smi::FromInt(context_list[i]->index()))); 873 scope_info_length++; 874 } 875 SetElementNonStrict(scope_info_list, 876 scope_info_length, 877 Handle<Object>(HEAP->null_value())); 878 scope_info_length++; 879 880 outer_scope = outer_scope->outer_scope(); 881 } while (outer_scope != NULL); 882 883 return *scope_info_list; 884 } 885 886 Handle<JSArray> result_; 887 int len_; 888 int current_parent_index_; 889 }; 890 891 892 JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script, 893 Handle<String> source) { 894 Isolate* isolate = Isolate::Current(); 895 ZoneScope zone_scope(isolate, DELETE_ON_EXIT); 896 897 FunctionInfoListener listener; 898 Handle<Object> original_source = Handle<Object>(script->source()); 899 script->set_source(*source); 900 isolate->set_active_function_info_listener(&listener); 901 CompileScriptForTracker(isolate, script); 902 isolate->set_active_function_info_listener(NULL); 903 script->set_source(*original_source); 904 905 return *(listener.GetResult()); 906 } 907 908 909 void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) { 910 HandleScope scope; 911 int len = Smi::cast(array->length())->value(); 912 for (int i = 0; i < len; i++) { 913 Handle<SharedFunctionInfo> info( 914 SharedFunctionInfo::cast(array->GetElementNoExceptionThrown(i))); 915 SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create(); 916 Handle<String> name_handle(String::cast(info->name())); 917 info_wrapper.SetProperties(name_handle, info->start_position(), 918 info->end_position(), info); 919 SetElementNonStrict(array, i, info_wrapper.GetJSArray()); 920 } 921 } 922 923 924 // Visitor that collects all references to a particular code object, 925 // including "CODE_TARGET" references in other code objects. 926 // It works in context of ZoneScope. 927 class ReferenceCollectorVisitor : public ObjectVisitor { 928 public: 929 explicit ReferenceCollectorVisitor(Code* original) 930 : original_(original), rvalues_(10), reloc_infos_(10), code_entries_(10) { 931 } 932 933 virtual void VisitPointers(Object** start, Object** end) { 934 for (Object** p = start; p < end; p++) { 935 if (*p == original_) { 936 rvalues_.Add(p); 937 } 938 } 939 } 940 941 virtual void VisitCodeEntry(Address entry) { 942 if (Code::GetObjectFromEntryAddress(entry) == original_) { 943 code_entries_.Add(entry); 944 } 945 } 946 947 virtual void VisitCodeTarget(RelocInfo* rinfo) { 948 if (RelocInfo::IsCodeTarget(rinfo->rmode()) && 949 Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) { 950 reloc_infos_.Add(*rinfo); 951 } 952 } 953 954 virtual void VisitDebugTarget(RelocInfo* rinfo) { 955 VisitCodeTarget(rinfo); 956 } 957 958 // Post-visiting method that iterates over all collected references and 959 // modifies them. 960 void Replace(Code* substitution) { 961 for (int i = 0; i < rvalues_.length(); i++) { 962 *(rvalues_[i]) = substitution; 963 } 964 Address substitution_entry = substitution->instruction_start(); 965 for (int i = 0; i < reloc_infos_.length(); i++) { 966 reloc_infos_[i].set_target_address(substitution_entry); 967 } 968 for (int i = 0; i < code_entries_.length(); i++) { 969 Address entry = code_entries_[i]; 970 Memory::Address_at(entry) = substitution_entry; 971 } 972 } 973 974 private: 975 Code* original_; 976 ZoneList<Object**> rvalues_; 977 ZoneList<RelocInfo> reloc_infos_; 978 ZoneList<Address> code_entries_; 979 }; 980 981 982 // Finds all references to original and replaces them with substitution. 983 static void ReplaceCodeObject(Code* original, Code* substitution) { 984 ASSERT(!HEAP->InNewSpace(substitution)); 985 986 HeapIterator iterator; 987 AssertNoAllocation no_allocations_please; 988 989 // A zone scope for ReferenceCollectorVisitor. 990 ZoneScope scope(Isolate::Current(), DELETE_ON_EXIT); 991 992 ReferenceCollectorVisitor visitor(original); 993 994 // Iterate over all roots. Stack frames may have pointer into original code, 995 // so temporary replace the pointers with offset numbers 996 // in prologue/epilogue. 997 { 998 HEAP->IterateStrongRoots(&visitor, VISIT_ALL); 999 } 1000 1001 // Now iterate over all pointers of all objects, including code_target 1002 // implicit pointers. 1003 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1004 obj->Iterate(&visitor); 1005 } 1006 1007 visitor.Replace(substitution); 1008 } 1009 1010 1011 // Check whether the code is natural function code (not a lazy-compile stub 1012 // code). 1013 static bool IsJSFunctionCode(Code* code) { 1014 return code->kind() == Code::FUNCTION; 1015 } 1016 1017 1018 // Returns true if an instance of candidate were inlined into function's code. 1019 static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) { 1020 AssertNoAllocation no_gc; 1021 1022 if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false; 1023 1024 DeoptimizationInputData* data = 1025 DeoptimizationInputData::cast(function->code()->deoptimization_data()); 1026 1027 if (data == HEAP->empty_fixed_array()) return false; 1028 1029 FixedArray* literals = data->LiteralArray(); 1030 1031 int inlined_count = data->InlinedFunctionCount()->value(); 1032 for (int i = 0; i < inlined_count; ++i) { 1033 JSFunction* inlined = JSFunction::cast(literals->get(i)); 1034 if (inlined->shared() == candidate) return true; 1035 } 1036 1037 return false; 1038 } 1039 1040 1041 class DependentFunctionsDeoptimizingVisitor : public OptimizedFunctionVisitor { 1042 public: 1043 explicit DependentFunctionsDeoptimizingVisitor( 1044 SharedFunctionInfo* function_info) 1045 : function_info_(function_info) {} 1046 1047 virtual void EnterContext(Context* context) { 1048 } 1049 1050 virtual void VisitFunction(JSFunction* function) { 1051 if (function->shared() == function_info_ || 1052 IsInlined(function, function_info_)) { 1053 Deoptimizer::DeoptimizeFunction(function); 1054 } 1055 } 1056 1057 virtual void LeaveContext(Context* context) { 1058 } 1059 1060 private: 1061 SharedFunctionInfo* function_info_; 1062 }; 1063 1064 1065 static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) { 1066 AssertNoAllocation no_allocation; 1067 1068 DependentFunctionsDeoptimizingVisitor visitor(function_info); 1069 Deoptimizer::VisitAllOptimizedFunctions(&visitor); 1070 } 1071 1072 1073 MaybeObject* LiveEdit::ReplaceFunctionCode( 1074 Handle<JSArray> new_compile_info_array, 1075 Handle<JSArray> shared_info_array) { 1076 HandleScope scope; 1077 1078 if (!SharedInfoWrapper::IsInstance(shared_info_array)) { 1079 return Isolate::Current()->ThrowIllegalOperation(); 1080 } 1081 1082 FunctionInfoWrapper compile_info_wrapper(new_compile_info_array); 1083 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1084 1085 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); 1086 1087 HEAP->EnsureHeapIsIterable(); 1088 1089 if (IsJSFunctionCode(shared_info->code())) { 1090 Handle<Code> code = compile_info_wrapper.GetFunctionCode(); 1091 ReplaceCodeObject(shared_info->code(), *code); 1092 Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo(); 1093 if (code_scope_info->IsFixedArray()) { 1094 shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info)); 1095 } 1096 } 1097 1098 if (shared_info->debug_info()->IsDebugInfo()) { 1099 Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info())); 1100 Handle<Code> new_original_code = 1101 FACTORY->CopyCode(compile_info_wrapper.GetFunctionCode()); 1102 debug_info->set_original_code(*new_original_code); 1103 } 1104 1105 int start_position = compile_info_wrapper.GetStartPosition(); 1106 int end_position = compile_info_wrapper.GetEndPosition(); 1107 shared_info->set_start_position(start_position); 1108 shared_info->set_end_position(end_position); 1109 1110 shared_info->set_construct_stub( 1111 Isolate::Current()->builtins()->builtin( 1112 Builtins::kJSConstructStubGeneric)); 1113 1114 DeoptimizeDependentFunctions(*shared_info); 1115 Isolate::Current()->compilation_cache()->Remove(shared_info); 1116 1117 return HEAP->undefined_value(); 1118 } 1119 1120 1121 MaybeObject* LiveEdit::FunctionSourceUpdated( 1122 Handle<JSArray> shared_info_array) { 1123 HandleScope scope; 1124 1125 if (!SharedInfoWrapper::IsInstance(shared_info_array)) { 1126 return Isolate::Current()->ThrowIllegalOperation(); 1127 } 1128 1129 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1130 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); 1131 1132 DeoptimizeDependentFunctions(*shared_info); 1133 Isolate::Current()->compilation_cache()->Remove(shared_info); 1134 1135 return HEAP->undefined_value(); 1136 } 1137 1138 1139 void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper, 1140 Handle<Object> script_handle) { 1141 Handle<SharedFunctionInfo> shared_info = 1142 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(function_wrapper)); 1143 shared_info->set_script(*script_handle); 1144 1145 Isolate::Current()->compilation_cache()->Remove(shared_info); 1146 } 1147 1148 1149 // For a script text change (defined as position_change_array), translates 1150 // position in unchanged text to position in changed text. 1151 // Text change is a set of non-overlapping regions in text, that have changed 1152 // their contents and length. It is specified as array of groups of 3 numbers: 1153 // (change_begin, change_end, change_end_new_position). 1154 // Each group describes a change in text; groups are sorted by change_begin. 1155 // Only position in text beyond any changes may be successfully translated. 1156 // If a positions is inside some region that changed, result is currently 1157 // undefined. 1158 static int TranslatePosition(int original_position, 1159 Handle<JSArray> position_change_array) { 1160 int position_diff = 0; 1161 int array_len = Smi::cast(position_change_array->length())->value(); 1162 // TODO(635): binary search may be used here 1163 for (int i = 0; i < array_len; i += 3) { 1164 Object* element = position_change_array->GetElementNoExceptionThrown(i); 1165 int chunk_start = Smi::cast(element)->value(); 1166 if (original_position < chunk_start) { 1167 break; 1168 } 1169 element = position_change_array->GetElementNoExceptionThrown(i + 1); 1170 int chunk_end = Smi::cast(element)->value(); 1171 // Position mustn't be inside a chunk. 1172 ASSERT(original_position >= chunk_end); 1173 element = position_change_array->GetElementNoExceptionThrown(i + 2); 1174 int chunk_changed_end = Smi::cast(element)->value(); 1175 position_diff = chunk_changed_end - chunk_end; 1176 } 1177 1178 return original_position + position_diff; 1179 } 1180 1181 1182 // Auto-growing buffer for writing relocation info code section. This buffer 1183 // is a simplified version of buffer from Assembler. Unlike Assembler, this 1184 // class is platform-independent and it works without dealing with instructions. 1185 // As specified by RelocInfo format, the buffer is filled in reversed order: 1186 // from upper to lower addresses. 1187 // It uses NewArray/DeleteArray for memory management. 1188 class RelocInfoBuffer { 1189 public: 1190 RelocInfoBuffer(int buffer_initial_capicity, byte* pc) { 1191 buffer_size_ = buffer_initial_capicity + kBufferGap; 1192 buffer_ = NewArray<byte>(buffer_size_); 1193 1194 reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc); 1195 } 1196 ~RelocInfoBuffer() { 1197 DeleteArray(buffer_); 1198 } 1199 1200 // As specified by RelocInfo format, the buffer is filled in reversed order: 1201 // from upper to lower addresses. 1202 void Write(const RelocInfo* rinfo) { 1203 if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) { 1204 Grow(); 1205 } 1206 reloc_info_writer_.Write(rinfo); 1207 } 1208 1209 Vector<byte> GetResult() { 1210 // Return the bytes from pos up to end of buffer. 1211 int result_size = 1212 static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos()); 1213 return Vector<byte>(reloc_info_writer_.pos(), result_size); 1214 } 1215 1216 private: 1217 void Grow() { 1218 // Compute new buffer size. 1219 int new_buffer_size; 1220 if (buffer_size_ < 2 * KB) { 1221 new_buffer_size = 4 * KB; 1222 } else { 1223 new_buffer_size = 2 * buffer_size_; 1224 } 1225 // Some internal data structures overflow for very large buffers, 1226 // they must ensure that kMaximalBufferSize is not too large. 1227 if (new_buffer_size > kMaximalBufferSize) { 1228 V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer"); 1229 } 1230 1231 // Set up new buffer. 1232 byte* new_buffer = NewArray<byte>(new_buffer_size); 1233 1234 // Copy the data. 1235 int curently_used_size = 1236 static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos()); 1237 memmove(new_buffer + new_buffer_size - curently_used_size, 1238 reloc_info_writer_.pos(), curently_used_size); 1239 1240 reloc_info_writer_.Reposition( 1241 new_buffer + new_buffer_size - curently_used_size, 1242 reloc_info_writer_.last_pc()); 1243 1244 DeleteArray(buffer_); 1245 buffer_ = new_buffer; 1246 buffer_size_ = new_buffer_size; 1247 } 1248 1249 RelocInfoWriter reloc_info_writer_; 1250 byte* buffer_; 1251 int buffer_size_; 1252 1253 static const int kBufferGap = RelocInfoWriter::kMaxSize; 1254 static const int kMaximalBufferSize = 512*MB; 1255 }; 1256 1257 // Patch positions in code (changes relocation info section) and possibly 1258 // returns new instance of code. 1259 static Handle<Code> PatchPositionsInCode( 1260 Handle<Code> code, 1261 Handle<JSArray> position_change_array) { 1262 1263 RelocInfoBuffer buffer_writer(code->relocation_size(), 1264 code->instruction_start()); 1265 1266 { 1267 AssertNoAllocation no_allocations_please; 1268 for (RelocIterator it(*code); !it.done(); it.next()) { 1269 RelocInfo* rinfo = it.rinfo(); 1270 if (RelocInfo::IsPosition(rinfo->rmode())) { 1271 int position = static_cast<int>(rinfo->data()); 1272 int new_position = TranslatePosition(position, 1273 position_change_array); 1274 if (position != new_position) { 1275 RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL); 1276 buffer_writer.Write(&info_copy); 1277 continue; 1278 } 1279 } 1280 buffer_writer.Write(it.rinfo()); 1281 } 1282 } 1283 1284 Vector<byte> buffer = buffer_writer.GetResult(); 1285 1286 if (buffer.length() == code->relocation_size()) { 1287 // Simply patch relocation area of code. 1288 memcpy(code->relocation_start(), buffer.start(), buffer.length()); 1289 return code; 1290 } else { 1291 // Relocation info section now has different size. We cannot simply 1292 // rewrite it inside code object. Instead we have to create a new 1293 // code object. 1294 Handle<Code> result(FACTORY->CopyCode(code, buffer)); 1295 return result; 1296 } 1297 } 1298 1299 1300 MaybeObject* LiveEdit::PatchFunctionPositions( 1301 Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) { 1302 1303 if (!SharedInfoWrapper::IsInstance(shared_info_array)) { 1304 return Isolate::Current()->ThrowIllegalOperation(); 1305 } 1306 1307 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1308 Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo(); 1309 1310 int old_function_start = info->start_position(); 1311 int new_function_start = TranslatePosition(old_function_start, 1312 position_change_array); 1313 int new_function_end = TranslatePosition(info->end_position(), 1314 position_change_array); 1315 int new_function_token_pos = 1316 TranslatePosition(info->function_token_position(), position_change_array); 1317 1318 info->set_start_position(new_function_start); 1319 info->set_end_position(new_function_end); 1320 info->set_function_token_position(new_function_token_pos); 1321 1322 HEAP->EnsureHeapIsIterable(); 1323 1324 if (IsJSFunctionCode(info->code())) { 1325 // Patch relocation info section of the code. 1326 Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()), 1327 position_change_array); 1328 if (*patched_code != info->code()) { 1329 // Replace all references to the code across the heap. In particular, 1330 // some stubs may refer to this code and this code may be being executed 1331 // on stack (it is safe to substitute the code object on stack, because 1332 // we only change the structure of rinfo and leave instructions 1333 // untouched). 1334 ReplaceCodeObject(info->code(), *patched_code); 1335 } 1336 } 1337 1338 return HEAP->undefined_value(); 1339 } 1340 1341 1342 static Handle<Script> CreateScriptCopy(Handle<Script> original) { 1343 Handle<String> original_source(String::cast(original->source())); 1344 1345 Handle<Script> copy = FACTORY->NewScript(original_source); 1346 1347 copy->set_name(original->name()); 1348 copy->set_line_offset(original->line_offset()); 1349 copy->set_column_offset(original->column_offset()); 1350 copy->set_data(original->data()); 1351 copy->set_type(original->type()); 1352 copy->set_context_data(original->context_data()); 1353 copy->set_compilation_type(original->compilation_type()); 1354 copy->set_eval_from_shared(original->eval_from_shared()); 1355 copy->set_eval_from_instructions_offset( 1356 original->eval_from_instructions_offset()); 1357 1358 return copy; 1359 } 1360 1361 1362 Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script, 1363 Handle<String> new_source, 1364 Handle<Object> old_script_name) { 1365 Handle<Object> old_script_object; 1366 if (old_script_name->IsString()) { 1367 Handle<Script> old_script = CreateScriptCopy(original_script); 1368 old_script->set_name(String::cast(*old_script_name)); 1369 old_script_object = old_script; 1370 Isolate::Current()->debugger()->OnAfterCompile( 1371 old_script, Debugger::SEND_WHEN_DEBUGGING); 1372 } else { 1373 old_script_object = Handle<Object>(HEAP->null_value()); 1374 } 1375 1376 original_script->set_source(*new_source); 1377 1378 // Drop line ends so that they will be recalculated. 1379 original_script->set_line_ends(HEAP->undefined_value()); 1380 1381 return *old_script_object; 1382 } 1383 1384 1385 1386 void LiveEdit::ReplaceRefToNestedFunction( 1387 Handle<JSValue> parent_function_wrapper, 1388 Handle<JSValue> orig_function_wrapper, 1389 Handle<JSValue> subst_function_wrapper) { 1390 1391 Handle<SharedFunctionInfo> parent_shared = 1392 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(parent_function_wrapper)); 1393 Handle<SharedFunctionInfo> orig_shared = 1394 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(orig_function_wrapper)); 1395 Handle<SharedFunctionInfo> subst_shared = 1396 Handle<SharedFunctionInfo>::cast(UnwrapJSValue(subst_function_wrapper)); 1397 1398 for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) { 1399 if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) { 1400 if (it.rinfo()->target_object() == *orig_shared) { 1401 it.rinfo()->set_target_object(*subst_shared); 1402 } 1403 } 1404 } 1405 } 1406 1407 1408 // Check an activation against list of functions. If there is a function 1409 // that matches, its status in result array is changed to status argument value. 1410 static bool CheckActivation(Handle<JSArray> shared_info_array, 1411 Handle<JSArray> result, 1412 StackFrame* frame, 1413 LiveEdit::FunctionPatchabilityStatus status) { 1414 if (!frame->is_java_script()) return false; 1415 1416 Handle<JSFunction> function( 1417 JSFunction::cast(JavaScriptFrame::cast(frame)->function())); 1418 1419 int len = Smi::cast(shared_info_array->length())->value(); 1420 for (int i = 0; i < len; i++) { 1421 JSValue* wrapper = 1422 JSValue::cast(shared_info_array->GetElementNoExceptionThrown(i)); 1423 Handle<SharedFunctionInfo> shared( 1424 SharedFunctionInfo::cast(wrapper->value())); 1425 1426 if (function->shared() == *shared || IsInlined(*function, *shared)) { 1427 SetElementNonStrict(result, i, Handle<Smi>(Smi::FromInt(status))); 1428 return true; 1429 } 1430 } 1431 return false; 1432 } 1433 1434 1435 // Iterates over handler chain and removes all elements that are inside 1436 // frames being dropped. 1437 static bool FixTryCatchHandler(StackFrame* top_frame, 1438 StackFrame* bottom_frame) { 1439 Address* pointer_address = 1440 &Memory::Address_at(Isolate::Current()->get_address_from_id( 1441 Isolate::kHandlerAddress)); 1442 1443 while (*pointer_address < top_frame->sp()) { 1444 pointer_address = &Memory::Address_at(*pointer_address); 1445 } 1446 Address* above_frame_address = pointer_address; 1447 while (*pointer_address < bottom_frame->fp()) { 1448 pointer_address = &Memory::Address_at(*pointer_address); 1449 } 1450 bool change = *above_frame_address != *pointer_address; 1451 *above_frame_address = *pointer_address; 1452 return change; 1453 } 1454 1455 1456 // Removes specified range of frames from stack. There may be 1 or more 1457 // frames in range. Anyway the bottom frame is restarted rather than dropped, 1458 // and therefore has to be a JavaScript frame. 1459 // Returns error message or NULL. 1460 static const char* DropFrames(Vector<StackFrame*> frames, 1461 int top_frame_index, 1462 int bottom_js_frame_index, 1463 Debug::FrameDropMode* mode, 1464 Object*** restarter_frame_function_pointer) { 1465 if (!Debug::kFrameDropperSupported) { 1466 return "Stack manipulations are not supported in this architecture."; 1467 } 1468 1469 StackFrame* pre_top_frame = frames[top_frame_index - 1]; 1470 StackFrame* top_frame = frames[top_frame_index]; 1471 StackFrame* bottom_js_frame = frames[bottom_js_frame_index]; 1472 1473 ASSERT(bottom_js_frame->is_java_script()); 1474 1475 // Check the nature of the top frame. 1476 Isolate* isolate = Isolate::Current(); 1477 Code* pre_top_frame_code = pre_top_frame->LookupCode(); 1478 if (pre_top_frame_code->is_inline_cache_stub() && 1479 pre_top_frame_code->ic_state() == DEBUG_BREAK) { 1480 // OK, we can drop inline cache calls. 1481 *mode = Debug::FRAME_DROPPED_IN_IC_CALL; 1482 } else if (pre_top_frame_code == 1483 isolate->debug()->debug_break_slot()) { 1484 // OK, we can drop debug break slot. 1485 *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL; 1486 } else if (pre_top_frame_code == 1487 isolate->builtins()->builtin( 1488 Builtins::kFrameDropper_LiveEdit)) { 1489 // OK, we can drop our own code. 1490 *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL; 1491 } else if (pre_top_frame_code == 1492 isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) { 1493 *mode = Debug::FRAME_DROPPED_IN_RETURN_CALL; 1494 } else if (pre_top_frame_code->kind() == Code::STUB && 1495 pre_top_frame_code->major_key()) { 1496 // Entry from our unit tests, it's fine, we support this case. 1497 *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL; 1498 } else { 1499 return "Unknown structure of stack above changing function"; 1500 } 1501 1502 Address unused_stack_top = top_frame->sp(); 1503 Address unused_stack_bottom = bottom_js_frame->fp() 1504 - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame. 1505 + kPointerSize; // Bigger address end is exclusive. 1506 1507 if (unused_stack_top > unused_stack_bottom) { 1508 return "Not enough space for frame dropper frame"; 1509 } 1510 1511 // Committing now. After this point we should return only NULL value. 1512 1513 FixTryCatchHandler(pre_top_frame, bottom_js_frame); 1514 // Make sure FixTryCatchHandler is idempotent. 1515 ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame)); 1516 1517 Handle<Code> code = Isolate::Current()->builtins()->FrameDropper_LiveEdit(); 1518 top_frame->set_pc(code->entry()); 1519 pre_top_frame->SetCallerFp(bottom_js_frame->fp()); 1520 1521 *restarter_frame_function_pointer = 1522 Debug::SetUpFrameDropperFrame(bottom_js_frame, code); 1523 1524 ASSERT((**restarter_frame_function_pointer)->IsJSFunction()); 1525 1526 for (Address a = unused_stack_top; 1527 a < unused_stack_bottom; 1528 a += kPointerSize) { 1529 Memory::Object_at(a) = Smi::FromInt(0); 1530 } 1531 1532 return NULL; 1533 } 1534 1535 1536 static bool IsDropableFrame(StackFrame* frame) { 1537 return !frame->is_exit(); 1538 } 1539 1540 // Fills result array with statuses of functions. Modifies the stack 1541 // removing all listed function if possible and if do_drop is true. 1542 static const char* DropActivationsInActiveThread( 1543 Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) { 1544 Isolate* isolate = Isolate::Current(); 1545 Debug* debug = isolate->debug(); 1546 ZoneScope scope(isolate, DELETE_ON_EXIT); 1547 Vector<StackFrame*> frames = CreateStackMap(); 1548 1549 int array_len = Smi::cast(shared_info_array->length())->value(); 1550 1551 int top_frame_index = -1; 1552 int frame_index = 0; 1553 for (; frame_index < frames.length(); frame_index++) { 1554 StackFrame* frame = frames[frame_index]; 1555 if (frame->id() == debug->break_frame_id()) { 1556 top_frame_index = frame_index; 1557 break; 1558 } 1559 if (CheckActivation(shared_info_array, result, frame, 1560 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { 1561 // We are still above break_frame. It is not a target frame, 1562 // it is a problem. 1563 return "Debugger mark-up on stack is not found"; 1564 } 1565 } 1566 1567 if (top_frame_index == -1) { 1568 // We haven't found break frame, but no function is blocking us anyway. 1569 return NULL; 1570 } 1571 1572 bool target_frame_found = false; 1573 int bottom_js_frame_index = top_frame_index; 1574 bool c_code_found = false; 1575 1576 for (; frame_index < frames.length(); frame_index++) { 1577 StackFrame* frame = frames[frame_index]; 1578 if (!IsDropableFrame(frame)) { 1579 c_code_found = true; 1580 break; 1581 } 1582 if (CheckActivation(shared_info_array, result, frame, 1583 LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { 1584 target_frame_found = true; 1585 bottom_js_frame_index = frame_index; 1586 } 1587 } 1588 1589 if (c_code_found) { 1590 // There is a C frames on stack. Check that there are no target frames 1591 // below them. 1592 for (; frame_index < frames.length(); frame_index++) { 1593 StackFrame* frame = frames[frame_index]; 1594 if (frame->is_java_script()) { 1595 if (CheckActivation(shared_info_array, result, frame, 1596 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { 1597 // Cannot drop frame under C frames. 1598 return NULL; 1599 } 1600 } 1601 } 1602 } 1603 1604 if (!do_drop) { 1605 // We are in check-only mode. 1606 return NULL; 1607 } 1608 1609 if (!target_frame_found) { 1610 // Nothing to drop. 1611 return NULL; 1612 } 1613 1614 Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED; 1615 Object** restarter_frame_function_pointer = NULL; 1616 const char* error_message = DropFrames(frames, top_frame_index, 1617 bottom_js_frame_index, &drop_mode, 1618 &restarter_frame_function_pointer); 1619 1620 if (error_message != NULL) { 1621 return error_message; 1622 } 1623 1624 // Adjust break_frame after some frames has been dropped. 1625 StackFrame::Id new_id = StackFrame::NO_ID; 1626 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) { 1627 if (frames[i]->type() == StackFrame::JAVA_SCRIPT) { 1628 new_id = frames[i]->id(); 1629 break; 1630 } 1631 } 1632 debug->FramesHaveBeenDropped(new_id, drop_mode, 1633 restarter_frame_function_pointer); 1634 1635 // Replace "blocked on active" with "replaced on active" status. 1636 for (int i = 0; i < array_len; i++) { 1637 if (result->GetElement(i) == 1638 Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { 1639 Handle<Object> replaced( 1640 Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK)); 1641 SetElementNonStrict(result, i, replaced); 1642 } 1643 } 1644 return NULL; 1645 } 1646 1647 1648 class InactiveThreadActivationsChecker : public ThreadVisitor { 1649 public: 1650 InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array, 1651 Handle<JSArray> result) 1652 : shared_info_array_(shared_info_array), result_(result), 1653 has_blocked_functions_(false) { 1654 } 1655 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { 1656 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { 1657 has_blocked_functions_ |= CheckActivation( 1658 shared_info_array_, result_, it.frame(), 1659 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK); 1660 } 1661 } 1662 bool HasBlockedFunctions() { 1663 return has_blocked_functions_; 1664 } 1665 1666 private: 1667 Handle<JSArray> shared_info_array_; 1668 Handle<JSArray> result_; 1669 bool has_blocked_functions_; 1670 }; 1671 1672 1673 Handle<JSArray> LiveEdit::CheckAndDropActivations( 1674 Handle<JSArray> shared_info_array, bool do_drop) { 1675 int len = Smi::cast(shared_info_array->length())->value(); 1676 1677 Handle<JSArray> result = FACTORY->NewJSArray(len); 1678 1679 // Fill the default values. 1680 for (int i = 0; i < len; i++) { 1681 SetElementNonStrict( 1682 result, 1683 i, 1684 Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH))); 1685 } 1686 1687 1688 // First check inactive threads. Fail if some functions are blocked there. 1689 InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array, 1690 result); 1691 Isolate::Current()->thread_manager()->IterateArchivedThreads( 1692 &inactive_threads_checker); 1693 if (inactive_threads_checker.HasBlockedFunctions()) { 1694 return result; 1695 } 1696 1697 // Try to drop activations from the current stack. 1698 const char* error_message = 1699 DropActivationsInActiveThread(shared_info_array, result, do_drop); 1700 if (error_message != NULL) { 1701 // Add error message as an array extra element. 1702 Vector<const char> vector_message(error_message, StrLength(error_message)); 1703 Handle<String> str = FACTORY->NewStringFromAscii(vector_message); 1704 SetElementNonStrict(result, len, str); 1705 } 1706 return result; 1707 } 1708 1709 1710 LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate, 1711 FunctionLiteral* fun) 1712 : isolate_(isolate) { 1713 if (isolate_->active_function_info_listener() != NULL) { 1714 isolate_->active_function_info_listener()->FunctionStarted(fun); 1715 } 1716 } 1717 1718 1719 LiveEditFunctionTracker::~LiveEditFunctionTracker() { 1720 if (isolate_->active_function_info_listener() != NULL) { 1721 isolate_->active_function_info_listener()->FunctionDone(); 1722 } 1723 } 1724 1725 1726 void LiveEditFunctionTracker::RecordFunctionInfo( 1727 Handle<SharedFunctionInfo> info, FunctionLiteral* lit) { 1728 if (isolate_->active_function_info_listener() != NULL) { 1729 isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope()); 1730 } 1731 } 1732 1733 1734 void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) { 1735 isolate_->active_function_info_listener()->FunctionCode(code); 1736 } 1737 1738 1739 bool LiveEditFunctionTracker::IsActive(Isolate* isolate) { 1740 return isolate->active_function_info_listener() != NULL; 1741 } 1742 1743 1744 #else // ENABLE_DEBUGGER_SUPPORT 1745 1746 // This ifdef-else-endif section provides working or stub implementation of 1747 // LiveEditFunctionTracker. 1748 LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate, 1749 FunctionLiteral* fun) { 1750 } 1751 1752 1753 LiveEditFunctionTracker::~LiveEditFunctionTracker() { 1754 } 1755 1756 1757 void LiveEditFunctionTracker::RecordFunctionInfo( 1758 Handle<SharedFunctionInfo> info, FunctionLiteral* lit) { 1759 } 1760 1761 1762 void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) { 1763 } 1764 1765 1766 bool LiveEditFunctionTracker::IsActive(Isolate* isolate) { 1767 return false; 1768 } 1769 1770 #endif // ENABLE_DEBUGGER_SUPPORT 1771 1772 1773 1774 } } // namespace v8::internal 1775