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