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 45namespace v8 { 46namespace internal { 47 48 49#ifdef ENABLE_DEBUGGER_SUPPORT 50 51 52void 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. 69class 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 265void 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 274static 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. 289class 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 296class 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 303static 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. 310static 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). 351class 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. 385class 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. 414class 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. 434class 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. 474class 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. 529class 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 586Handle<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 605static 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" 627static 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. 634static Handle<JSValue> WrapInJSValue(Handle<HeapObject> object) { 635 Isolate* isolate = object->GetIsolate(); 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 644static 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 652static 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. 662template<typename S> 663class JSArrayBasedStruct { 664 public: 665 static S Create(Isolate* isolate) { 666 Factory* factory = isolate->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(isolate(), 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. 710class 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<HeapObject> 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 SetFunctionScopeInfo(Handle<Object> scope_info_array) { 735 this->SetField(kFunctionScopeInfoOffset_, 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 kFunctionScopeInfoOffset_ = 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. 787class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> { 788 public: 789 static bool IsInstance(Handle<JSArray> array) { 790 return array->length() == Smi::FromInt(kSize_) && 791 array->GetElementNoExceptionThrown( 792 array->GetIsolate(), kSharedInfoOffset_)->IsJSValue(); 793 } 794 795 explicit SharedInfoWrapper(Handle<JSArray> array) 796 : JSArrayBasedStruct<SharedInfoWrapper>(array) { 797 } 798 799 void SetProperties(Handle<String> name, int start_position, int end_position, 800 Handle<SharedFunctionInfo> info) { 801 HandleScope scope(isolate()); 802 this->SetField(kFunctionNameOffset_, name); 803 Handle<JSValue> info_holder = WrapInJSValue(info); 804 this->SetField(kSharedInfoOffset_, info_holder); 805 this->SetSmiValueField(kStartPositionOffset_, start_position); 806 this->SetSmiValueField(kEndPositionOffset_, end_position); 807 } 808 Handle<SharedFunctionInfo> GetInfo() { 809 Object* element = this->GetField(kSharedInfoOffset_); 810 CHECK(element->IsJSValue()); 811 Handle<JSValue> value_wrapper(JSValue::cast(element)); 812 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper); 813 } 814 815 private: 816 static const int kFunctionNameOffset_ = 0; 817 static const int kStartPositionOffset_ = 1; 818 static const int kEndPositionOffset_ = 2; 819 static const int kSharedInfoOffset_ = 3; 820 static const int kSize_ = 4; 821 822 friend class JSArrayBasedStruct<SharedInfoWrapper>; 823}; 824 825 826class FunctionInfoListener { 827 public: 828 explicit FunctionInfoListener(Isolate* isolate) { 829 current_parent_index_ = -1; 830 len_ = 0; 831 result_ = isolate->factory()->NewJSArray(10); 832 } 833 834 void FunctionStarted(FunctionLiteral* fun) { 835 HandleScope scope(isolate()); 836 FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate()); 837 info.SetInitialProperties(fun->name(), fun->start_position(), 838 fun->end_position(), fun->parameter_count(), 839 fun->materialized_literal_count(), 840 current_parent_index_); 841 current_parent_index_ = len_; 842 SetElementNonStrict(result_, len_, info.GetJSArray()); 843 len_++; 844 } 845 846 void FunctionDone() { 847 HandleScope scope(isolate()); 848 FunctionInfoWrapper info = 849 FunctionInfoWrapper::cast( 850 result_->GetElementNoExceptionThrown( 851 isolate(), current_parent_index_)); 852 current_parent_index_ = info.GetParentIndex(); 853 } 854 855 // Saves only function code, because for a script function we 856 // may never create a SharedFunctionInfo object. 857 void FunctionCode(Handle<Code> function_code) { 858 FunctionInfoWrapper info = 859 FunctionInfoWrapper::cast( 860 result_->GetElementNoExceptionThrown( 861 isolate(), current_parent_index_)); 862 info.SetFunctionCode(function_code, 863 Handle<HeapObject>(isolate()->heap()->null_value())); 864 } 865 866 // Saves full information about a function: its code, its scope info 867 // and a SharedFunctionInfo object. 868 void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope, 869 Zone* zone) { 870 if (!shared->IsSharedFunctionInfo()) { 871 return; 872 } 873 FunctionInfoWrapper info = 874 FunctionInfoWrapper::cast( 875 result_->GetElementNoExceptionThrown( 876 isolate(), current_parent_index_)); 877 info.SetFunctionCode(Handle<Code>(shared->code()), 878 Handle<HeapObject>(shared->scope_info())); 879 info.SetSharedFunctionInfo(shared); 880 881 Handle<Object> scope_info_list(SerializeFunctionScope(scope, zone), 882 isolate()); 883 info.SetFunctionScopeInfo(scope_info_list); 884 } 885 886 Handle<JSArray> GetResult() { return result_; } 887 888 private: 889 Isolate* isolate() const { return result_->GetIsolate(); } 890 891 Object* SerializeFunctionScope(Scope* scope, Zone* zone) { 892 HandleScope handle_scope(isolate()); 893 894 Handle<JSArray> scope_info_list = isolate()->factory()->NewJSArray(10); 895 int scope_info_length = 0; 896 897 // Saves some description of scope. It stores name and indexes of 898 // variables in the whole scope chain. Null-named slots delimit 899 // scopes of this chain. 900 Scope* current_scope = scope; 901 while (current_scope != NULL) { 902 ZoneList<Variable*> stack_list(current_scope->StackLocalCount(), zone); 903 ZoneList<Variable*> context_list( 904 current_scope->ContextLocalCount(), zone); 905 current_scope->CollectStackAndContextLocals(&stack_list, &context_list); 906 context_list.Sort(&Variable::CompareIndex); 907 908 for (int i = 0; i < context_list.length(); i++) { 909 SetElementNonStrict(scope_info_list, 910 scope_info_length, 911 context_list[i]->name()); 912 scope_info_length++; 913 SetElementNonStrict( 914 scope_info_list, 915 scope_info_length, 916 Handle<Smi>(Smi::FromInt(context_list[i]->index()), isolate())); 917 scope_info_length++; 918 } 919 SetElementNonStrict(scope_info_list, 920 scope_info_length, 921 Handle<Object>(isolate()->heap()->null_value(), 922 isolate())); 923 scope_info_length++; 924 925 current_scope = current_scope->outer_scope(); 926 } 927 928 return *scope_info_list; 929 } 930 931 Handle<JSArray> result_; 932 int len_; 933 int current_parent_index_; 934}; 935 936 937JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script, 938 Handle<String> source) { 939 Isolate* isolate = script->GetIsolate(); 940 941 FunctionInfoListener listener(isolate); 942 Handle<Object> original_source = 943 Handle<Object>(script->source(), isolate); 944 script->set_source(*source); 945 isolate->set_active_function_info_listener(&listener); 946 947 { 948 // Creating verbose TryCatch from public API is currently the only way to 949 // force code save location. We do not use this the object directly. 950 v8::TryCatch try_catch; 951 try_catch.SetVerbose(true); 952 953 // A logical 'try' section. 954 CompileScriptForTracker(isolate, script); 955 } 956 957 // A logical 'catch' section. 958 Handle<JSObject> rethrow_exception; 959 if (isolate->has_pending_exception()) { 960 Handle<Object> exception(isolate->pending_exception()->ToObjectChecked(), 961 isolate); 962 MessageLocation message_location = isolate->GetMessageLocation(); 963 964 isolate->clear_pending_message(); 965 isolate->clear_pending_exception(); 966 967 // If possible, copy positions from message object to exception object. 968 if (exception->IsJSObject() && !message_location.script().is_null()) { 969 rethrow_exception = Handle<JSObject>::cast(exception); 970 971 Factory* factory = isolate->factory(); 972 Handle<String> start_pos_key = factory->InternalizeOneByteString( 973 STATIC_ASCII_VECTOR("startPosition")); 974 Handle<String> end_pos_key = factory->InternalizeOneByteString( 975 STATIC_ASCII_VECTOR("endPosition")); 976 Handle<String> script_obj_key = factory->InternalizeOneByteString( 977 STATIC_ASCII_VECTOR("scriptObject")); 978 Handle<Smi> start_pos( 979 Smi::FromInt(message_location.start_pos()), isolate); 980 Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate); 981 Handle<JSValue> script_obj = GetScriptWrapper(message_location.script()); 982 JSReceiver::SetProperty( 983 rethrow_exception, start_pos_key, start_pos, NONE, kNonStrictMode); 984 JSReceiver::SetProperty( 985 rethrow_exception, end_pos_key, end_pos, NONE, kNonStrictMode); 986 JSReceiver::SetProperty( 987 rethrow_exception, script_obj_key, script_obj, NONE, kNonStrictMode); 988 } 989 } 990 991 // A logical 'finally' section. 992 isolate->set_active_function_info_listener(NULL); 993 script->set_source(*original_source); 994 995 if (rethrow_exception.is_null()) { 996 return *(listener.GetResult()); 997 } else { 998 isolate->Throw(*rethrow_exception); 999 return 0; 1000 } 1001} 1002 1003 1004void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) { 1005 Isolate* isolate = array->GetIsolate(); 1006 HandleScope scope(isolate); 1007 int len = GetArrayLength(array); 1008 for (int i = 0; i < len; i++) { 1009 Handle<SharedFunctionInfo> info( 1010 SharedFunctionInfo::cast( 1011 array->GetElementNoExceptionThrown(isolate, i))); 1012 SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create(isolate); 1013 Handle<String> name_handle(String::cast(info->name())); 1014 info_wrapper.SetProperties(name_handle, info->start_position(), 1015 info->end_position(), info); 1016 SetElementNonStrict(array, i, info_wrapper.GetJSArray()); 1017 } 1018} 1019 1020 1021// Visitor that finds all references to a particular code object, 1022// including "CODE_TARGET" references in other code objects and replaces 1023// them on the fly. 1024class ReplacingVisitor : public ObjectVisitor { 1025 public: 1026 explicit ReplacingVisitor(Code* original, Code* substitution) 1027 : original_(original), substitution_(substitution) { 1028 } 1029 1030 virtual void VisitPointers(Object** start, Object** end) { 1031 for (Object** p = start; p < end; p++) { 1032 if (*p == original_) { 1033 *p = substitution_; 1034 } 1035 } 1036 } 1037 1038 virtual void VisitCodeEntry(Address entry) { 1039 if (Code::GetObjectFromEntryAddress(entry) == original_) { 1040 Address substitution_entry = substitution_->instruction_start(); 1041 Memory::Address_at(entry) = substitution_entry; 1042 } 1043 } 1044 1045 virtual void VisitCodeTarget(RelocInfo* rinfo) { 1046 if (RelocInfo::IsCodeTarget(rinfo->rmode()) && 1047 Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) { 1048 Address substitution_entry = substitution_->instruction_start(); 1049 rinfo->set_target_address(substitution_entry); 1050 } 1051 } 1052 1053 virtual void VisitDebugTarget(RelocInfo* rinfo) { 1054 VisitCodeTarget(rinfo); 1055 } 1056 1057 private: 1058 Code* original_; 1059 Code* substitution_; 1060}; 1061 1062 1063// Finds all references to original and replaces them with substitution. 1064static void ReplaceCodeObject(Handle<Code> original, 1065 Handle<Code> substitution) { 1066 // Perform a full GC in order to ensure that we are not in the middle of an 1067 // incremental marking phase when we are replacing the code object. 1068 // Since we are not in an incremental marking phase we can write pointers 1069 // to code objects (that are never in new space) without worrying about 1070 // write barriers. 1071 Heap* heap = original->GetHeap(); 1072 heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, 1073 "liveedit.cc ReplaceCodeObject"); 1074 1075 ASSERT(!heap->InNewSpace(*substitution)); 1076 1077 DisallowHeapAllocation no_allocation; 1078 1079 ReplacingVisitor visitor(*original, *substitution); 1080 1081 // Iterate over all roots. Stack frames may have pointer into original code, 1082 // so temporary replace the pointers with offset numbers 1083 // in prologue/epilogue. 1084 heap->IterateRoots(&visitor, VISIT_ALL); 1085 1086 // Now iterate over all pointers of all objects, including code_target 1087 // implicit pointers. 1088 HeapIterator iterator(heap); 1089 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1090 obj->Iterate(&visitor); 1091 } 1092} 1093 1094 1095// Patch function literals. 1096// Name 'literals' is a misnomer. Rather it's a cache for complex object 1097// boilerplates and for a native context. We must clean cached values. 1098// Additionally we may need to allocate a new array if number of literals 1099// changed. 1100class LiteralFixer { 1101 public: 1102 static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper, 1103 Handle<SharedFunctionInfo> shared_info, 1104 Isolate* isolate) { 1105 int new_literal_count = compile_info_wrapper->GetLiteralCount(); 1106 if (new_literal_count > 0) { 1107 new_literal_count += JSFunction::kLiteralsPrefixSize; 1108 } 1109 int old_literal_count = shared_info->num_literals(); 1110 1111 if (old_literal_count == new_literal_count) { 1112 // If literal count didn't change, simply go over all functions 1113 // and clear literal arrays. 1114 ClearValuesVisitor visitor; 1115 IterateJSFunctions(*shared_info, &visitor); 1116 } else { 1117 // When literal count changes, we have to create new array instances. 1118 // Since we cannot create instances when iterating heap, we should first 1119 // collect all functions and fix their literal arrays. 1120 Handle<FixedArray> function_instances = 1121 CollectJSFunctions(shared_info, isolate); 1122 for (int i = 0; i < function_instances->length(); i++) { 1123 Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i))); 1124 Handle<FixedArray> old_literals(fun->literals()); 1125 Handle<FixedArray> new_literals = 1126 isolate->factory()->NewFixedArray(new_literal_count); 1127 if (new_literal_count > 0) { 1128 Handle<Context> native_context; 1129 if (old_literals->length() > 1130 JSFunction::kLiteralNativeContextIndex) { 1131 native_context = Handle<Context>( 1132 JSFunction::NativeContextFromLiterals(fun->literals())); 1133 } else { 1134 native_context = Handle<Context>(fun->context()->native_context()); 1135 } 1136 new_literals->set(JSFunction::kLiteralNativeContextIndex, 1137 *native_context); 1138 } 1139 fun->set_literals(*new_literals); 1140 } 1141 1142 shared_info->set_num_literals(new_literal_count); 1143 } 1144 } 1145 1146 private: 1147 // Iterates all function instances in the HEAP that refers to the 1148 // provided shared_info. 1149 template<typename Visitor> 1150 static void IterateJSFunctions(SharedFunctionInfo* shared_info, 1151 Visitor* visitor) { 1152 DisallowHeapAllocation no_allocation; 1153 1154 HeapIterator iterator(shared_info->GetHeap()); 1155 for (HeapObject* obj = iterator.next(); obj != NULL; 1156 obj = iterator.next()) { 1157 if (obj->IsJSFunction()) { 1158 JSFunction* function = JSFunction::cast(obj); 1159 if (function->shared() == shared_info) { 1160 visitor->visit(function); 1161 } 1162 } 1163 } 1164 } 1165 1166 // Finds all instances of JSFunction that refers to the provided shared_info 1167 // and returns array with them. 1168 static Handle<FixedArray> CollectJSFunctions( 1169 Handle<SharedFunctionInfo> shared_info, Isolate* isolate) { 1170 CountVisitor count_visitor; 1171 count_visitor.count = 0; 1172 IterateJSFunctions(*shared_info, &count_visitor); 1173 int size = count_visitor.count; 1174 1175 Handle<FixedArray> result = isolate->factory()->NewFixedArray(size); 1176 if (size > 0) { 1177 CollectVisitor collect_visitor(result); 1178 IterateJSFunctions(*shared_info, &collect_visitor); 1179 } 1180 return result; 1181 } 1182 1183 class ClearValuesVisitor { 1184 public: 1185 void visit(JSFunction* fun) { 1186 FixedArray* literals = fun->literals(); 1187 int len = literals->length(); 1188 for (int j = JSFunction::kLiteralsPrefixSize; j < len; j++) { 1189 literals->set_undefined(j); 1190 } 1191 } 1192 }; 1193 1194 class CountVisitor { 1195 public: 1196 void visit(JSFunction* fun) { 1197 count++; 1198 } 1199 int count; 1200 }; 1201 1202 class CollectVisitor { 1203 public: 1204 explicit CollectVisitor(Handle<FixedArray> output) 1205 : m_output(output), m_pos(0) {} 1206 1207 void visit(JSFunction* fun) { 1208 m_output->set(m_pos, fun); 1209 m_pos++; 1210 } 1211 private: 1212 Handle<FixedArray> m_output; 1213 int m_pos; 1214 }; 1215}; 1216 1217 1218// Check whether the code is natural function code (not a lazy-compile stub 1219// code). 1220static bool IsJSFunctionCode(Code* code) { 1221 return code->kind() == Code::FUNCTION; 1222} 1223 1224 1225// Returns true if an instance of candidate were inlined into function's code. 1226static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) { 1227 DisallowHeapAllocation no_gc; 1228 1229 if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false; 1230 1231 DeoptimizationInputData* data = 1232 DeoptimizationInputData::cast(function->code()->deoptimization_data()); 1233 1234 if (data == function->GetIsolate()->heap()->empty_fixed_array()) { 1235 return false; 1236 } 1237 1238 FixedArray* literals = data->LiteralArray(); 1239 1240 int inlined_count = data->InlinedFunctionCount()->value(); 1241 for (int i = 0; i < inlined_count; ++i) { 1242 JSFunction* inlined = JSFunction::cast(literals->get(i)); 1243 if (inlined->shared() == candidate) return true; 1244 } 1245 1246 return false; 1247} 1248 1249 1250// Marks code that shares the same shared function info or has inlined 1251// code that shares the same function info. 1252class DependentFunctionMarker: public OptimizedFunctionVisitor { 1253 public: 1254 SharedFunctionInfo* shared_info_; 1255 bool found_; 1256 1257 explicit DependentFunctionMarker(SharedFunctionInfo* shared_info) 1258 : shared_info_(shared_info), found_(false) { } 1259 1260 virtual void EnterContext(Context* context) { } // Don't care. 1261 virtual void LeaveContext(Context* context) { } // Don't care. 1262 virtual void VisitFunction(JSFunction* function) { 1263 // It should be guaranteed by the iterator that everything is optimized. 1264 ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION); 1265 if (shared_info_ == function->shared() || 1266 IsInlined(function, shared_info_)) { 1267 // Mark the code for deoptimization. 1268 function->code()->set_marked_for_deoptimization(true); 1269 found_ = true; 1270 } 1271 } 1272}; 1273 1274 1275static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) { 1276 DisallowHeapAllocation no_allocation; 1277 DependentFunctionMarker marker(function_info); 1278 // TODO(titzer): need to traverse all optimized code to find OSR code here. 1279 Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker); 1280 1281 if (marker.found_) { 1282 // Only go through with the deoptimization if something was found. 1283 Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate()); 1284 } 1285} 1286 1287 1288MaybeObject* LiveEdit::ReplaceFunctionCode( 1289 Handle<JSArray> new_compile_info_array, 1290 Handle<JSArray> shared_info_array) { 1291 Isolate* isolate = new_compile_info_array->GetIsolate(); 1292 HandleScope scope(isolate); 1293 1294 if (!SharedInfoWrapper::IsInstance(shared_info_array)) { 1295 return isolate->ThrowIllegalOperation(); 1296 } 1297 1298 FunctionInfoWrapper compile_info_wrapper(new_compile_info_array); 1299 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1300 1301 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); 1302 1303 isolate->heap()->EnsureHeapIsIterable(); 1304 1305 if (IsJSFunctionCode(shared_info->code())) { 1306 Handle<Code> code = compile_info_wrapper.GetFunctionCode(); 1307 ReplaceCodeObject(Handle<Code>(shared_info->code()), code); 1308 Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo(); 1309 if (code_scope_info->IsFixedArray()) { 1310 shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info)); 1311 } 1312 shared_info->DisableOptimization(kLiveEdit); 1313 } 1314 1315 if (shared_info->debug_info()->IsDebugInfo()) { 1316 Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info())); 1317 Handle<Code> new_original_code = 1318 isolate->factory()->CopyCode(compile_info_wrapper.GetFunctionCode()); 1319 debug_info->set_original_code(*new_original_code); 1320 } 1321 1322 int start_position = compile_info_wrapper.GetStartPosition(); 1323 int end_position = compile_info_wrapper.GetEndPosition(); 1324 shared_info->set_start_position(start_position); 1325 shared_info->set_end_position(end_position); 1326 1327 LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate); 1328 1329 shared_info->set_construct_stub( 1330 isolate->builtins()->builtin(Builtins::kJSConstructStubGeneric)); 1331 1332 DeoptimizeDependentFunctions(*shared_info); 1333 isolate->compilation_cache()->Remove(shared_info); 1334 1335 return isolate->heap()->undefined_value(); 1336} 1337 1338 1339MaybeObject* LiveEdit::FunctionSourceUpdated( 1340 Handle<JSArray> shared_info_array) { 1341 Isolate* isolate = shared_info_array->GetIsolate(); 1342 HandleScope scope(isolate); 1343 1344 if (!SharedInfoWrapper::IsInstance(shared_info_array)) { 1345 return isolate->ThrowIllegalOperation(); 1346 } 1347 1348 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1349 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); 1350 1351 DeoptimizeDependentFunctions(*shared_info); 1352 isolate->compilation_cache()->Remove(shared_info); 1353 1354 return isolate->heap()->undefined_value(); 1355} 1356 1357 1358void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper, 1359 Handle<Object> script_handle) { 1360 Handle<SharedFunctionInfo> shared_info = 1361 UnwrapSharedFunctionInfoFromJSValue(function_wrapper); 1362 CHECK(script_handle->IsScript() || script_handle->IsUndefined()); 1363 shared_info->set_script(*script_handle); 1364 1365 function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info); 1366} 1367 1368 1369// For a script text change (defined as position_change_array), translates 1370// position in unchanged text to position in changed text. 1371// Text change is a set of non-overlapping regions in text, that have changed 1372// their contents and length. It is specified as array of groups of 3 numbers: 1373// (change_begin, change_end, change_end_new_position). 1374// Each group describes a change in text; groups are sorted by change_begin. 1375// Only position in text beyond any changes may be successfully translated. 1376// If a positions is inside some region that changed, result is currently 1377// undefined. 1378static int TranslatePosition(int original_position, 1379 Handle<JSArray> position_change_array) { 1380 int position_diff = 0; 1381 int array_len = GetArrayLength(position_change_array); 1382 Isolate* isolate = position_change_array->GetIsolate(); 1383 // TODO(635): binary search may be used here 1384 for (int i = 0; i < array_len; i += 3) { 1385 Object* element = 1386 position_change_array->GetElementNoExceptionThrown(isolate, i); 1387 CHECK(element->IsSmi()); 1388 int chunk_start = Smi::cast(element)->value(); 1389 if (original_position < chunk_start) { 1390 break; 1391 } 1392 element = position_change_array->GetElementNoExceptionThrown(isolate, 1393 i + 1); 1394 CHECK(element->IsSmi()); 1395 int chunk_end = Smi::cast(element)->value(); 1396 // Position mustn't be inside a chunk. 1397 ASSERT(original_position >= chunk_end); 1398 element = position_change_array->GetElementNoExceptionThrown(isolate, 1399 i + 2); 1400 CHECK(element->IsSmi()); 1401 int chunk_changed_end = Smi::cast(element)->value(); 1402 position_diff = chunk_changed_end - chunk_end; 1403 } 1404 1405 return original_position + position_diff; 1406} 1407 1408 1409// Auto-growing buffer for writing relocation info code section. This buffer 1410// is a simplified version of buffer from Assembler. Unlike Assembler, this 1411// class is platform-independent and it works without dealing with instructions. 1412// As specified by RelocInfo format, the buffer is filled in reversed order: 1413// from upper to lower addresses. 1414// It uses NewArray/DeleteArray for memory management. 1415class RelocInfoBuffer { 1416 public: 1417 RelocInfoBuffer(int buffer_initial_capicity, byte* pc) { 1418 buffer_size_ = buffer_initial_capicity + kBufferGap; 1419 buffer_ = NewArray<byte>(buffer_size_); 1420 1421 reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc); 1422 } 1423 ~RelocInfoBuffer() { 1424 DeleteArray(buffer_); 1425 } 1426 1427 // As specified by RelocInfo format, the buffer is filled in reversed order: 1428 // from upper to lower addresses. 1429 void Write(const RelocInfo* rinfo) { 1430 if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) { 1431 Grow(); 1432 } 1433 reloc_info_writer_.Write(rinfo); 1434 } 1435 1436 Vector<byte> GetResult() { 1437 // Return the bytes from pos up to end of buffer. 1438 int result_size = 1439 static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos()); 1440 return Vector<byte>(reloc_info_writer_.pos(), result_size); 1441 } 1442 1443 private: 1444 void Grow() { 1445 // Compute new buffer size. 1446 int new_buffer_size; 1447 if (buffer_size_ < 2 * KB) { 1448 new_buffer_size = 4 * KB; 1449 } else { 1450 new_buffer_size = 2 * buffer_size_; 1451 } 1452 // Some internal data structures overflow for very large buffers, 1453 // they must ensure that kMaximalBufferSize is not too large. 1454 if (new_buffer_size > kMaximalBufferSize) { 1455 V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer"); 1456 } 1457 1458 // Set up new buffer. 1459 byte* new_buffer = NewArray<byte>(new_buffer_size); 1460 1461 // Copy the data. 1462 int curently_used_size = 1463 static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos()); 1464 OS::MemMove(new_buffer + new_buffer_size - curently_used_size, 1465 reloc_info_writer_.pos(), curently_used_size); 1466 1467 reloc_info_writer_.Reposition( 1468 new_buffer + new_buffer_size - curently_used_size, 1469 reloc_info_writer_.last_pc()); 1470 1471 DeleteArray(buffer_); 1472 buffer_ = new_buffer; 1473 buffer_size_ = new_buffer_size; 1474 } 1475 1476 RelocInfoWriter reloc_info_writer_; 1477 byte* buffer_; 1478 int buffer_size_; 1479 1480 static const int kBufferGap = RelocInfoWriter::kMaxSize; 1481 static const int kMaximalBufferSize = 512*MB; 1482}; 1483 1484 1485// Patch positions in code (changes relocation info section) and possibly 1486// returns new instance of code. 1487static Handle<Code> PatchPositionsInCode( 1488 Handle<Code> code, 1489 Handle<JSArray> position_change_array) { 1490 Isolate* isolate = code->GetIsolate(); 1491 1492 RelocInfoBuffer buffer_writer(code->relocation_size(), 1493 code->instruction_start()); 1494 1495 { 1496 DisallowHeapAllocation no_allocation; 1497 for (RelocIterator it(*code); !it.done(); it.next()) { 1498 RelocInfo* rinfo = it.rinfo(); 1499 if (RelocInfo::IsPosition(rinfo->rmode())) { 1500 int position = static_cast<int>(rinfo->data()); 1501 int new_position = TranslatePosition(position, 1502 position_change_array); 1503 if (position != new_position) { 1504 RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL); 1505 buffer_writer.Write(&info_copy); 1506 continue; 1507 } 1508 } 1509 if (RelocInfo::IsRealRelocMode(rinfo->rmode())) { 1510 buffer_writer.Write(it.rinfo()); 1511 } 1512 } 1513 } 1514 1515 Vector<byte> buffer = buffer_writer.GetResult(); 1516 1517 if (buffer.length() == code->relocation_size()) { 1518 // Simply patch relocation area of code. 1519 OS::MemCopy(code->relocation_start(), buffer.start(), buffer.length()); 1520 return code; 1521 } else { 1522 // Relocation info section now has different size. We cannot simply 1523 // rewrite it inside code object. Instead we have to create a new 1524 // code object. 1525 Handle<Code> result(isolate->factory()->CopyCode(code, buffer)); 1526 return result; 1527 } 1528} 1529 1530 1531MaybeObject* LiveEdit::PatchFunctionPositions( 1532 Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) { 1533 if (!SharedInfoWrapper::IsInstance(shared_info_array)) { 1534 return shared_info_array->GetIsolate()->ThrowIllegalOperation(); 1535 } 1536 1537 SharedInfoWrapper shared_info_wrapper(shared_info_array); 1538 Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo(); 1539 1540 int old_function_start = info->start_position(); 1541 int new_function_start = TranslatePosition(old_function_start, 1542 position_change_array); 1543 int new_function_end = TranslatePosition(info->end_position(), 1544 position_change_array); 1545 int new_function_token_pos = 1546 TranslatePosition(info->function_token_position(), position_change_array); 1547 1548 info->set_start_position(new_function_start); 1549 info->set_end_position(new_function_end); 1550 info->set_function_token_position(new_function_token_pos); 1551 1552 info->GetIsolate()->heap()->EnsureHeapIsIterable(); 1553 1554 if (IsJSFunctionCode(info->code())) { 1555 // Patch relocation info section of the code. 1556 Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()), 1557 position_change_array); 1558 if (*patched_code != info->code()) { 1559 // Replace all references to the code across the heap. In particular, 1560 // some stubs may refer to this code and this code may be being executed 1561 // on stack (it is safe to substitute the code object on stack, because 1562 // we only change the structure of rinfo and leave instructions 1563 // untouched). 1564 ReplaceCodeObject(Handle<Code>(info->code()), patched_code); 1565 } 1566 } 1567 1568 return info->GetIsolate()->heap()->undefined_value(); 1569} 1570 1571 1572static Handle<Script> CreateScriptCopy(Handle<Script> original) { 1573 Isolate* isolate = original->GetIsolate(); 1574 1575 Handle<String> original_source(String::cast(original->source())); 1576 Handle<Script> copy = isolate->factory()->NewScript(original_source); 1577 1578 copy->set_name(original->name()); 1579 copy->set_line_offset(original->line_offset()); 1580 copy->set_column_offset(original->column_offset()); 1581 copy->set_data(original->data()); 1582 copy->set_type(original->type()); 1583 copy->set_context_data(original->context_data()); 1584 copy->set_eval_from_shared(original->eval_from_shared()); 1585 copy->set_eval_from_instructions_offset( 1586 original->eval_from_instructions_offset()); 1587 1588 // Copy all the flags, but clear compilation state. 1589 copy->set_flags(original->flags()); 1590 copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL); 1591 1592 return copy; 1593} 1594 1595 1596Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script, 1597 Handle<String> new_source, 1598 Handle<Object> old_script_name) { 1599 Isolate* isolate = original_script->GetIsolate(); 1600 Handle<Object> old_script_object; 1601 if (old_script_name->IsString()) { 1602 Handle<Script> old_script = CreateScriptCopy(original_script); 1603 old_script->set_name(String::cast(*old_script_name)); 1604 old_script_object = old_script; 1605 isolate->debugger()->OnAfterCompile( 1606 old_script, Debugger::SEND_WHEN_DEBUGGING); 1607 } else { 1608 old_script_object = isolate->factory()->null_value(); 1609 } 1610 1611 original_script->set_source(*new_source); 1612 1613 // Drop line ends so that they will be recalculated. 1614 original_script->set_line_ends(isolate->heap()->undefined_value()); 1615 1616 return *old_script_object; 1617} 1618 1619 1620 1621void LiveEdit::ReplaceRefToNestedFunction( 1622 Handle<JSValue> parent_function_wrapper, 1623 Handle<JSValue> orig_function_wrapper, 1624 Handle<JSValue> subst_function_wrapper) { 1625 1626 Handle<SharedFunctionInfo> parent_shared = 1627 UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper); 1628 Handle<SharedFunctionInfo> orig_shared = 1629 UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper); 1630 Handle<SharedFunctionInfo> subst_shared = 1631 UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper); 1632 1633 for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) { 1634 if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) { 1635 if (it.rinfo()->target_object() == *orig_shared) { 1636 it.rinfo()->set_target_object(*subst_shared); 1637 } 1638 } 1639 } 1640} 1641 1642 1643// Check an activation against list of functions. If there is a function 1644// that matches, its status in result array is changed to status argument value. 1645static bool CheckActivation(Handle<JSArray> shared_info_array, 1646 Handle<JSArray> result, 1647 StackFrame* frame, 1648 LiveEdit::FunctionPatchabilityStatus status) { 1649 if (!frame->is_java_script()) return false; 1650 1651 Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function()); 1652 1653 Isolate* isolate = shared_info_array->GetIsolate(); 1654 int len = GetArrayLength(shared_info_array); 1655 for (int i = 0; i < len; i++) { 1656 Object* element = 1657 shared_info_array->GetElementNoExceptionThrown(isolate, i); 1658 CHECK(element->IsJSValue()); 1659 Handle<JSValue> jsvalue(JSValue::cast(element)); 1660 Handle<SharedFunctionInfo> shared = 1661 UnwrapSharedFunctionInfoFromJSValue(jsvalue); 1662 1663 if (function->shared() == *shared || IsInlined(*function, *shared)) { 1664 SetElementNonStrict(result, i, Handle<Smi>(Smi::FromInt(status), 1665 isolate)); 1666 return true; 1667 } 1668 } 1669 return false; 1670} 1671 1672 1673// Iterates over handler chain and removes all elements that are inside 1674// frames being dropped. 1675static bool FixTryCatchHandler(StackFrame* top_frame, 1676 StackFrame* bottom_frame) { 1677 Address* pointer_address = 1678 &Memory::Address_at(top_frame->isolate()->get_address_from_id( 1679 Isolate::kHandlerAddress)); 1680 1681 while (*pointer_address < top_frame->sp()) { 1682 pointer_address = &Memory::Address_at(*pointer_address); 1683 } 1684 Address* above_frame_address = pointer_address; 1685 while (*pointer_address < bottom_frame->fp()) { 1686 pointer_address = &Memory::Address_at(*pointer_address); 1687 } 1688 bool change = *above_frame_address != *pointer_address; 1689 *above_frame_address = *pointer_address; 1690 return change; 1691} 1692 1693 1694// Removes specified range of frames from stack. There may be 1 or more 1695// frames in range. Anyway the bottom frame is restarted rather than dropped, 1696// and therefore has to be a JavaScript frame. 1697// Returns error message or NULL. 1698static const char* DropFrames(Vector<StackFrame*> frames, 1699 int top_frame_index, 1700 int bottom_js_frame_index, 1701 Debug::FrameDropMode* mode, 1702 Object*** restarter_frame_function_pointer) { 1703 if (!Debug::kFrameDropperSupported) { 1704 return "Stack manipulations are not supported in this architecture."; 1705 } 1706 1707 StackFrame* pre_top_frame = frames[top_frame_index - 1]; 1708 StackFrame* top_frame = frames[top_frame_index]; 1709 StackFrame* bottom_js_frame = frames[bottom_js_frame_index]; 1710 1711 ASSERT(bottom_js_frame->is_java_script()); 1712 1713 // Check the nature of the top frame. 1714 Isolate* isolate = bottom_js_frame->isolate(); 1715 Code* pre_top_frame_code = pre_top_frame->LookupCode(); 1716 bool frame_has_padding; 1717 if (pre_top_frame_code->is_inline_cache_stub() && 1718 pre_top_frame_code->is_debug_stub()) { 1719 // OK, we can drop inline cache calls. 1720 *mode = Debug::FRAME_DROPPED_IN_IC_CALL; 1721 frame_has_padding = Debug::FramePaddingLayout::kIsSupported; 1722 } else if (pre_top_frame_code == 1723 isolate->debug()->debug_break_slot()) { 1724 // OK, we can drop debug break slot. 1725 *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL; 1726 frame_has_padding = Debug::FramePaddingLayout::kIsSupported; 1727 } else if (pre_top_frame_code == 1728 isolate->builtins()->builtin( 1729 Builtins::kFrameDropper_LiveEdit)) { 1730 // OK, we can drop our own code. 1731 pre_top_frame = frames[top_frame_index - 2]; 1732 top_frame = frames[top_frame_index - 1]; 1733 *mode = Debug::CURRENTLY_SET_MODE; 1734 frame_has_padding = false; 1735 } else if (pre_top_frame_code == 1736 isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) { 1737 *mode = Debug::FRAME_DROPPED_IN_RETURN_CALL; 1738 frame_has_padding = Debug::FramePaddingLayout::kIsSupported; 1739 } else if (pre_top_frame_code->kind() == Code::STUB && 1740 pre_top_frame_code->major_key() == CodeStub::CEntry) { 1741 // Entry from our unit tests on 'debugger' statement. 1742 // It's fine, we support this case. 1743 *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL; 1744 // We don't have a padding from 'debugger' statement call. 1745 // Here the stub is CEntry, it's not debug-only and can't be padded. 1746 // If anyone would complain, a proxy padded stub could be added. 1747 frame_has_padding = false; 1748 } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) { 1749 // This must be adaptor that remain from the frame dropping that 1750 // is still on stack. A frame dropper frame must be above it. 1751 ASSERT(frames[top_frame_index - 2]->LookupCode() == 1752 isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)); 1753 pre_top_frame = frames[top_frame_index - 3]; 1754 top_frame = frames[top_frame_index - 2]; 1755 *mode = Debug::CURRENTLY_SET_MODE; 1756 frame_has_padding = false; 1757 } else { 1758 return "Unknown structure of stack above changing function"; 1759 } 1760 1761 Address unused_stack_top = top_frame->sp(); 1762 Address unused_stack_bottom = bottom_js_frame->fp() 1763 - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame. 1764 + kPointerSize; // Bigger address end is exclusive. 1765 1766 Address* top_frame_pc_address = top_frame->pc_address(); 1767 1768 // top_frame may be damaged below this point. Do not used it. 1769 ASSERT(!(top_frame = NULL)); 1770 1771 if (unused_stack_top > unused_stack_bottom) { 1772 if (frame_has_padding) { 1773 int shortage_bytes = 1774 static_cast<int>(unused_stack_top - unused_stack_bottom); 1775 1776 Address padding_start = pre_top_frame->fp() - 1777 Debug::FramePaddingLayout::kFrameBaseSize * kPointerSize; 1778 1779 Address padding_pointer = padding_start; 1780 Smi* padding_object = 1781 Smi::FromInt(Debug::FramePaddingLayout::kPaddingValue); 1782 while (Memory::Object_at(padding_pointer) == padding_object) { 1783 padding_pointer -= kPointerSize; 1784 } 1785 int padding_counter = 1786 Smi::cast(Memory::Object_at(padding_pointer))->value(); 1787 if (padding_counter * kPointerSize < shortage_bytes) { 1788 return "Not enough space for frame dropper frame " 1789 "(even with padding frame)"; 1790 } 1791 Memory::Object_at(padding_pointer) = 1792 Smi::FromInt(padding_counter - shortage_bytes / kPointerSize); 1793 1794 StackFrame* pre_pre_frame = frames[top_frame_index - 2]; 1795 1796 OS::MemMove(padding_start + kPointerSize - shortage_bytes, 1797 padding_start + kPointerSize, 1798 Debug::FramePaddingLayout::kFrameBaseSize * kPointerSize); 1799 1800 pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes); 1801 pre_pre_frame->SetCallerFp(pre_top_frame->fp()); 1802 unused_stack_top -= shortage_bytes; 1803 1804 STATIC_ASSERT(sizeof(Address) == kPointerSize); 1805 top_frame_pc_address -= shortage_bytes / kPointerSize; 1806 } else { 1807 return "Not enough space for frame dropper frame"; 1808 } 1809 } 1810 1811 // Committing now. After this point we should return only NULL value. 1812 1813 FixTryCatchHandler(pre_top_frame, bottom_js_frame); 1814 // Make sure FixTryCatchHandler is idempotent. 1815 ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame)); 1816 1817 Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit(); 1818 *top_frame_pc_address = code->entry(); 1819 pre_top_frame->SetCallerFp(bottom_js_frame->fp()); 1820 1821 *restarter_frame_function_pointer = 1822 Debug::SetUpFrameDropperFrame(bottom_js_frame, code); 1823 1824 ASSERT((**restarter_frame_function_pointer)->IsJSFunction()); 1825 1826 for (Address a = unused_stack_top; 1827 a < unused_stack_bottom; 1828 a += kPointerSize) { 1829 Memory::Object_at(a) = Smi::FromInt(0); 1830 } 1831 1832 return NULL; 1833} 1834 1835 1836static bool IsDropableFrame(StackFrame* frame) { 1837 return !frame->is_exit(); 1838} 1839 1840 1841// Describes a set of call frames that execute any of listed functions. 1842// Finding no such frames does not mean error. 1843class MultipleFunctionTarget { 1844 public: 1845 MultipleFunctionTarget(Handle<JSArray> shared_info_array, 1846 Handle<JSArray> result) 1847 : m_shared_info_array(shared_info_array), 1848 m_result(result) {} 1849 bool MatchActivation(StackFrame* frame, 1850 LiveEdit::FunctionPatchabilityStatus status) { 1851 return CheckActivation(m_shared_info_array, m_result, frame, status); 1852 } 1853 const char* GetNotFoundMessage() { 1854 return NULL; 1855 } 1856 private: 1857 Handle<JSArray> m_shared_info_array; 1858 Handle<JSArray> m_result; 1859}; 1860 1861 1862// Drops all call frame matched by target and all frames above them. 1863template<typename TARGET> 1864static const char* DropActivationsInActiveThreadImpl( 1865 Isolate* isolate, TARGET& target, bool do_drop) { 1866 Debug* debug = isolate->debug(); 1867 Zone zone(isolate); 1868 Vector<StackFrame*> frames = CreateStackMap(isolate, &zone); 1869 1870 1871 int top_frame_index = -1; 1872 int frame_index = 0; 1873 for (; frame_index < frames.length(); frame_index++) { 1874 StackFrame* frame = frames[frame_index]; 1875 if (frame->id() == debug->break_frame_id()) { 1876 top_frame_index = frame_index; 1877 break; 1878 } 1879 if (target.MatchActivation( 1880 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { 1881 // We are still above break_frame. It is not a target frame, 1882 // it is a problem. 1883 return "Debugger mark-up on stack is not found"; 1884 } 1885 } 1886 1887 if (top_frame_index == -1) { 1888 // We haven't found break frame, but no function is blocking us anyway. 1889 return target.GetNotFoundMessage(); 1890 } 1891 1892 bool target_frame_found = false; 1893 int bottom_js_frame_index = top_frame_index; 1894 bool c_code_found = false; 1895 1896 for (; frame_index < frames.length(); frame_index++) { 1897 StackFrame* frame = frames[frame_index]; 1898 if (!IsDropableFrame(frame)) { 1899 c_code_found = true; 1900 break; 1901 } 1902 if (target.MatchActivation( 1903 frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { 1904 target_frame_found = true; 1905 bottom_js_frame_index = frame_index; 1906 } 1907 } 1908 1909 if (c_code_found) { 1910 // There is a C frames on stack. Check that there are no target frames 1911 // below them. 1912 for (; frame_index < frames.length(); frame_index++) { 1913 StackFrame* frame = frames[frame_index]; 1914 if (frame->is_java_script()) { 1915 if (target.MatchActivation( 1916 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { 1917 // Cannot drop frame under C frames. 1918 return NULL; 1919 } 1920 } 1921 } 1922 } 1923 1924 if (!do_drop) { 1925 // We are in check-only mode. 1926 return NULL; 1927 } 1928 1929 if (!target_frame_found) { 1930 // Nothing to drop. 1931 return target.GetNotFoundMessage(); 1932 } 1933 1934 Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED; 1935 Object** restarter_frame_function_pointer = NULL; 1936 const char* error_message = DropFrames(frames, top_frame_index, 1937 bottom_js_frame_index, &drop_mode, 1938 &restarter_frame_function_pointer); 1939 1940 if (error_message != NULL) { 1941 return error_message; 1942 } 1943 1944 // Adjust break_frame after some frames has been dropped. 1945 StackFrame::Id new_id = StackFrame::NO_ID; 1946 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) { 1947 if (frames[i]->type() == StackFrame::JAVA_SCRIPT) { 1948 new_id = frames[i]->id(); 1949 break; 1950 } 1951 } 1952 debug->FramesHaveBeenDropped(new_id, drop_mode, 1953 restarter_frame_function_pointer); 1954 return NULL; 1955} 1956 1957 1958// Fills result array with statuses of functions. Modifies the stack 1959// removing all listed function if possible and if do_drop is true. 1960static const char* DropActivationsInActiveThread( 1961 Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) { 1962 MultipleFunctionTarget target(shared_info_array, result); 1963 1964 const char* message = DropActivationsInActiveThreadImpl( 1965 shared_info_array->GetIsolate(), target, do_drop); 1966 if (message) { 1967 return message; 1968 } 1969 1970 Isolate* isolate = shared_info_array->GetIsolate(); 1971 int array_len = GetArrayLength(shared_info_array); 1972 1973 // Replace "blocked on active" with "replaced on active" status. 1974 for (int i = 0; i < array_len; i++) { 1975 if (result->GetElement(result->GetIsolate(), i) == 1976 Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { 1977 Handle<Object> replaced( 1978 Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate); 1979 SetElementNonStrict(result, i, replaced); 1980 } 1981 } 1982 return NULL; 1983} 1984 1985 1986class InactiveThreadActivationsChecker : public ThreadVisitor { 1987 public: 1988 InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array, 1989 Handle<JSArray> result) 1990 : shared_info_array_(shared_info_array), result_(result), 1991 has_blocked_functions_(false) { 1992 } 1993 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { 1994 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { 1995 has_blocked_functions_ |= CheckActivation( 1996 shared_info_array_, result_, it.frame(), 1997 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK); 1998 } 1999 } 2000 bool HasBlockedFunctions() { 2001 return has_blocked_functions_; 2002 } 2003 2004 private: 2005 Handle<JSArray> shared_info_array_; 2006 Handle<JSArray> result_; 2007 bool has_blocked_functions_; 2008}; 2009 2010 2011Handle<JSArray> LiveEdit::CheckAndDropActivations( 2012 Handle<JSArray> shared_info_array, bool do_drop) { 2013 Isolate* isolate = shared_info_array->GetIsolate(); 2014 int len = GetArrayLength(shared_info_array); 2015 2016 Handle<JSArray> result = isolate->factory()->NewJSArray(len); 2017 2018 // Fill the default values. 2019 for (int i = 0; i < len; i++) { 2020 SetElementNonStrict( 2021 result, 2022 i, 2023 Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH), isolate)); 2024 } 2025 2026 2027 // First check inactive threads. Fail if some functions are blocked there. 2028 InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array, 2029 result); 2030 isolate->thread_manager()->IterateArchivedThreads( 2031 &inactive_threads_checker); 2032 if (inactive_threads_checker.HasBlockedFunctions()) { 2033 return result; 2034 } 2035 2036 // Try to drop activations from the current stack. 2037 const char* error_message = 2038 DropActivationsInActiveThread(shared_info_array, result, do_drop); 2039 if (error_message != NULL) { 2040 // Add error message as an array extra element. 2041 Vector<const char> vector_message(error_message, StrLength(error_message)); 2042 Handle<String> str = isolate->factory()->NewStringFromAscii(vector_message); 2043 SetElementNonStrict(result, len, str); 2044 } 2045 return result; 2046} 2047 2048 2049// Describes a single callframe a target. Not finding this frame 2050// means an error. 2051class SingleFrameTarget { 2052 public: 2053 explicit SingleFrameTarget(JavaScriptFrame* frame) 2054 : m_frame(frame), 2055 m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {} 2056 2057 bool MatchActivation(StackFrame* frame, 2058 LiveEdit::FunctionPatchabilityStatus status) { 2059 if (frame->fp() == m_frame->fp()) { 2060 m_saved_status = status; 2061 return true; 2062 } 2063 return false; 2064 } 2065 const char* GetNotFoundMessage() { 2066 return "Failed to found requested frame"; 2067 } 2068 LiveEdit::FunctionPatchabilityStatus saved_status() { 2069 return m_saved_status; 2070 } 2071 private: 2072 JavaScriptFrame* m_frame; 2073 LiveEdit::FunctionPatchabilityStatus m_saved_status; 2074}; 2075 2076 2077// Finds a drops required frame and all frames above. 2078// Returns error message or NULL. 2079const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) { 2080 SingleFrameTarget target(frame); 2081 2082 const char* result = DropActivationsInActiveThreadImpl( 2083 frame->isolate(), target, true); 2084 if (result != NULL) { 2085 return result; 2086 } 2087 if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) { 2088 return "Function is blocked under native code"; 2089 } 2090 return NULL; 2091} 2092 2093 2094LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate, 2095 FunctionLiteral* fun) 2096 : isolate_(isolate) { 2097 if (isolate_->active_function_info_listener() != NULL) { 2098 isolate_->active_function_info_listener()->FunctionStarted(fun); 2099 } 2100} 2101 2102 2103LiveEditFunctionTracker::~LiveEditFunctionTracker() { 2104 if (isolate_->active_function_info_listener() != NULL) { 2105 isolate_->active_function_info_listener()->FunctionDone(); 2106 } 2107} 2108 2109 2110void LiveEditFunctionTracker::RecordFunctionInfo( 2111 Handle<SharedFunctionInfo> info, FunctionLiteral* lit, 2112 Zone* zone) { 2113 if (isolate_->active_function_info_listener() != NULL) { 2114 isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope(), 2115 zone); 2116 } 2117} 2118 2119 2120void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) { 2121 isolate_->active_function_info_listener()->FunctionCode(code); 2122} 2123 2124 2125bool LiveEditFunctionTracker::IsActive(Isolate* isolate) { 2126 return isolate->active_function_info_listener() != NULL; 2127} 2128 2129 2130#else // ENABLE_DEBUGGER_SUPPORT 2131 2132// This ifdef-else-endif section provides working or stub implementation of 2133// LiveEditFunctionTracker. 2134LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate, 2135 FunctionLiteral* fun) { 2136} 2137 2138 2139LiveEditFunctionTracker::~LiveEditFunctionTracker() { 2140} 2141 2142 2143void LiveEditFunctionTracker::RecordFunctionInfo( 2144 Handle<SharedFunctionInfo> info, FunctionLiteral* lit, 2145 Zone* zone) { 2146} 2147 2148 2149void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) { 2150} 2151 2152 2153bool LiveEditFunctionTracker::IsActive(Isolate* isolate) { 2154 return false; 2155} 2156 2157#endif // ENABLE_DEBUGGER_SUPPORT 2158 2159 2160 2161} } // namespace v8::internal 2162