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