5 #include "src/debug/liveedit.h" 7 #include "src/api-inl.h" 8 #include "src/ast/ast-traversal-visitor.h" 9 #include "src/ast/ast.h" 10 #include "src/ast/scopes.h" 11 #include "src/compilation-cache.h" 12 #include "src/compiler.h" 13 #include "src/debug/debug-interface.h" 14 #include "src/debug/debug.h" 15 #include "src/frames-inl.h" 16 #include "src/isolate-inl.h" 17 #include "src/objects-inl.h" 18 #include "src/objects/hash-table-inl.h" 19 #include "src/objects/js-generator-inl.h" 20 #include "src/parsing/parse-info.h" 21 #include "src/parsing/parsing.h" 22 #include "src/source-position-table.h" 35 virtual int GetLength1() = 0;
36 virtual int GetLength2() = 0;
37 virtual bool Equals(
int index1,
int index2) = 0;
40 virtual ~Input() =
default;
48 virtual void AddChunk(
int pos1,
int pos2,
int len1,
int len2) = 0;
51 virtual ~Output() =
default;
55 static void CalculateDifference(Input* input, Output* result_writer);
64 explicit Differencer(Comparator::Input* input)
65 : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
66 buffer_ = NewArray<int>(len1_ * len2_);
73 int array_size = len1_ * len2_;
74 for (
int i = 0;
i < array_size;
i++) {
75 buffer_[
i] = kEmptyCellValue;
82 CompareUpToTail(0, 0);
85 void SaveResult(Comparator::Output* chunk_writer) {
86 ResultWriter writer(chunk_writer);
93 Direction dir = get_direction(pos1, pos2);
113 writer.skip1(len1_ - pos1);
118 writer.skip2(len2_ - pos2);
127 Comparator::Input* input_;
138 MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
143 int CompareUpToTail(
int pos1,
int pos2) {
146 int cached_res = get_value4(pos1, pos2);
147 if (cached_res == kEmptyCellValue) {
150 if (input_->Equals(pos1, pos2)) {
151 res = CompareUpToTail(pos1 + 1, pos2 + 1);
154 int res1 = CompareUpToTail(pos1 + 1, pos2) +
155 (1 << kDirectionSizeBits);
156 int res2 = CompareUpToTail(pos1, pos2 + 1) +
157 (1 << kDirectionSizeBits);
161 }
else if (res1 < res2) {
169 set_value4_and_dir(pos1, pos2, res, dir);
174 return (len1_ - pos1) << kDirectionSizeBits;
177 return (len2_ - pos2) << kDirectionSizeBits;
181 inline int& get_cell(
int i1,
int i2) {
182 return buffer_[i1 + i2 * len1_];
186 void set_value4_and_dir(
int i1,
int i2,
int value4, Direction dir) {
187 DCHECK_EQ(0, value4 & kDirectionMask);
188 get_cell(i1, i2) = value4 | dir;
191 int get_value4(
int i1,
int i2) {
192 return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
194 Direction get_direction(
int i1,
int i2) {
195 return static_cast<Direction
>(get_cell(i1, i2) & kDirectionMask);
198 static const int kDirectionSizeBits = 2;
199 static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
200 static const int kEmptyCellValue = ~0u << kDirectionSizeBits;
204 void StaticAssertHolder() {
205 STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
210 explicit ResultWriter(Comparator::Output* chunk_writer)
211 : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
212 pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
219 void skip1(
int len1) {
223 void skip2(
int len2) {
232 Comparator::Output* chunk_writer_;
237 bool has_open_chunk_;
240 if (!has_open_chunk_) {
243 has_open_chunk_ =
true;
248 if (has_open_chunk_) {
249 chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
250 pos1_ - pos1_begin_, pos2_ - pos2_begin_);
251 has_open_chunk_ =
false;
257 void Comparator::CalculateDifference(Comparator::Input* input,
258 Comparator::Output* result_writer) {
259 Differencer differencer(input);
260 differencer.Initialize();
261 differencer.FillTable();
262 differencer.SaveResult(result_writer);
265 bool CompareSubstrings(Handle<String> s1,
int pos1, Handle<String> s2,
int pos2,
267 for (
int i = 0;
i < len;
i++) {
268 if (s1->Get(
i + pos1) != s2->Get(
i + pos2))
return false;
277 class SubrangableInput :
public Comparator::Input {
279 virtual void SetSubrange1(
int offset,
int len) = 0;
280 virtual void SetSubrange2(
int offset,
int len) = 0;
284 class SubrangableOutput :
public Comparator::Output {
286 virtual void SetSubrange1(
int offset,
int len) = 0;
287 virtual void SetSubrange2(
int offset,
int len) = 0;
292 void NarrowDownInput(SubrangableInput* input, SubrangableOutput* output) {
293 const int len1 = input->GetLength1();
294 const int len2 = input->GetLength2();
296 int common_prefix_len;
297 int common_suffix_len;
300 common_prefix_len = 0;
301 int prefix_limit = std::min(len1, len2);
302 while (common_prefix_len < prefix_limit &&
303 input->Equals(common_prefix_len, common_prefix_len)) {
307 common_suffix_len = 0;
309 std::min(len1 - common_prefix_len, len2 - common_prefix_len);
311 while (common_suffix_len < suffix_limit &&
312 input->Equals(len1 - common_suffix_len - 1,
313 len2 - common_suffix_len - 1)) {
318 if (common_prefix_len > 0 || common_suffix_len > 0) {
319 int new_len1 = len1 - common_suffix_len - common_prefix_len;
320 int new_len2 = len2 - common_suffix_len - common_prefix_len;
322 input->SetSubrange1(common_prefix_len, new_len1);
323 input->SetSubrange2(common_prefix_len, new_len2);
325 output->SetSubrange1(common_prefix_len, new_len1);
326 output->SetSubrange2(common_prefix_len, new_len2);
333 class TokensCompareInput :
public Comparator::Input {
335 TokensCompareInput(Handle<String> s1,
int offset1,
int len1,
336 Handle<String> s2,
int offset2,
int len2)
337 : s1_(s1), offset1_(offset1), len1_(len1),
338 s2_(s2), offset2_(offset2), len2_(len2) {
340 int GetLength1()
override {
return len1_; }
341 int GetLength2()
override {
return len2_; }
342 bool Equals(
int index1,
int index2)
override {
343 return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
357 class TokensCompareOutput :
public Comparator::Output {
359 TokensCompareOutput(
int offset1,
int offset2,
360 std::vector<SourceChangeRange>* output)
361 : output_(output), offset1_(offset1), offset2_(offset2) {}
363 void AddChunk(
int pos1,
int pos2,
int len1,
int len2)
override {
364 output_->emplace_back(
365 SourceChangeRange{pos1 + offset1_, pos1 + len1 + offset1_,
366 pos2 + offset2_, pos2 + offset2_ + len2});
370 std::vector<SourceChangeRange>* output_;
377 class LineEndsWrapper {
379 explicit LineEndsWrapper(Isolate* isolate, Handle<String>
string)
380 : ends_array_(String::CalculateLineEnds(isolate, string, false)),
381 string_len_(string->length()) {}
383 return ends_array_->length() + 1;
387 int GetLineStart(
int index) {
return index == 0 ? 0 : GetLineEnd(index - 1); }
388 int GetLineEnd(
int index) {
389 if (index == ends_array_->length()) {
395 return GetPosAfterNewLine(index);
400 Handle<FixedArray> ends_array_;
403 int GetPosAfterNewLine(
int index) {
404 return Smi::ToInt(ends_array_->get(index)) + 1;
409 class LineArrayCompareInput :
public SubrangableInput {
411 LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
412 LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
413 : s1_(s1), s2_(s2), line_ends1_(line_ends1),
414 line_ends2_(line_ends2),
415 subrange_offset1_(0), subrange_offset2_(0),
416 subrange_len1_(line_ends1_.length()),
417 subrange_len2_(line_ends2_.length()) {
419 int GetLength1()
override {
return subrange_len1_; }
420 int GetLength2()
override {
return subrange_len2_; }
421 bool Equals(
int index1,
int index2)
override {
422 index1 += subrange_offset1_;
423 index2 += subrange_offset2_;
425 int line_start1 = line_ends1_.GetLineStart(index1);
426 int line_start2 = line_ends2_.GetLineStart(index2);
427 int line_end1 = line_ends1_.GetLineEnd(index1);
428 int line_end2 = line_ends2_.GetLineEnd(index2);
429 int len1 = line_end1 - line_start1;
430 int len2 = line_end2 - line_start2;
434 return CompareSubstrings(s1_, line_start1, s2_, line_start2,
437 void SetSubrange1(
int offset,
int len)
override {
438 subrange_offset1_ = offset;
439 subrange_len1_ = len;
441 void SetSubrange2(
int offset,
int len)
override {
442 subrange_offset2_ = offset;
443 subrange_len2_ = len;
449 LineEndsWrapper line_ends1_;
450 LineEndsWrapper line_ends2_;
451 int subrange_offset1_;
452 int subrange_offset2_;
459 class TokenizingLineArrayCompareOutput :
public SubrangableOutput {
461 TokenizingLineArrayCompareOutput(Isolate* isolate, LineEndsWrapper line_ends1,
462 LineEndsWrapper line_ends2,
463 Handle<String> s1, Handle<String> s2,
464 std::vector<SourceChangeRange>* output)
466 line_ends1_(line_ends1),
467 line_ends2_(line_ends2),
470 subrange_offset1_(0),
471 subrange_offset2_(0),
474 void AddChunk(
int line_pos1,
int line_pos2,
int line_len1,
475 int line_len2)
override {
476 line_pos1 += subrange_offset1_;
477 line_pos2 += subrange_offset2_;
479 int char_pos1 = line_ends1_.GetLineStart(line_pos1);
480 int char_pos2 = line_ends2_.GetLineStart(line_pos2);
481 int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
482 int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
484 if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
486 HandleScope subTaskScope(isolate_);
488 TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
489 s2_, char_pos2, char_len2);
490 TokensCompareOutput tokens_output(char_pos1, char_pos2, output_);
492 Comparator::CalculateDifference(&tokens_input, &tokens_output);
494 output_->emplace_back(SourceChangeRange{
495 char_pos1, char_pos1 + char_len1, char_pos2, char_pos2 + char_len2});
498 void SetSubrange1(
int offset,
int len)
override {
499 subrange_offset1_ = offset;
501 void SetSubrange2(
int offset,
int len)
override {
502 subrange_offset2_ = offset;
506 static const int CHUNK_LEN_LIMIT = 800;
509 LineEndsWrapper line_ends1_;
510 LineEndsWrapper line_ends2_;
513 int subrange_offset1_;
514 int subrange_offset2_;
515 std::vector<SourceChangeRange>* output_;
518 struct SourcePositionEvent {
519 enum Type { LITERAL_STARTS, LITERAL_ENDS, DIFF_STARTS, DIFF_ENDS };
525 FunctionLiteral* literal;
529 SourcePositionEvent(FunctionLiteral* literal,
bool is_start)
530 : position(is_start ? literal->start_position()
531 : literal->end_position()),
532 type(is_start ? LITERAL_STARTS : LITERAL_ENDS),
534 SourcePositionEvent(
const SourceChangeRange& change,
bool is_start)
535 : position(is_start ? change.start_position : change.end_position),
536 type(is_start ? DIFF_STARTS : DIFF_ENDS),
537 pos_diff((change.new_end_position - change.new_start_position) -
538 (change.end_position - change.start_position)) {}
540 static bool LessThan(
const SourcePositionEvent& a,
541 const SourcePositionEvent& b) {
542 if (a.position != b.position)
return a.position < b.position;
543 if (a.type != b.type)
return a.type < b.type;
544 if (a.type == LITERAL_STARTS && b.type == LITERAL_STARTS) {
547 if (a.literal->end_position() != b.literal->end_position()) {
548 return a.literal->end_position() > b.literal->end_position();
552 return a.literal->function_literal_id() <
553 b.literal->function_literal_id();
554 }
else if (a.type == LITERAL_ENDS && b.type == LITERAL_ENDS) {
557 if (a.literal->start_position() != b.literal->start_position()) {
558 return a.literal->start_position() > b.literal->start_position();
562 return a.literal->function_literal_id() >
563 b.literal->function_literal_id();
565 return a.pos_diff < b.pos_diff;
570 struct FunctionLiteralChange {
573 int new_start_position;
574 int new_end_position;
576 FunctionLiteral* outer_literal;
578 explicit FunctionLiteralChange(
int new_start_position, FunctionLiteral* outer)
579 : new_start_position(new_start_position),
580 new_end_position(kNoSourcePosition),
582 outer_literal(outer) {}
585 using FunctionLiteralChanges =
586 std::unordered_map<FunctionLiteral*, FunctionLiteralChange>;
587 void CalculateFunctionLiteralChanges(
588 const std::vector<FunctionLiteral*>& literals,
589 const std::vector<SourceChangeRange>& diffs,
590 FunctionLiteralChanges* result) {
591 std::vector<SourcePositionEvent> events;
592 events.reserve(literals.size() * 2 + diffs.size() * 2);
593 for (FunctionLiteral* literal : literals) {
594 events.emplace_back(literal,
true);
595 events.emplace_back(literal,
false);
597 for (
const SourceChangeRange& diff : diffs) {
598 events.emplace_back(diff,
true);
599 events.emplace_back(diff,
false);
601 std::sort(events.begin(), events.end(), SourcePositionEvent::LessThan);
602 bool inside_diff =
false;
604 std::stack<std::pair<FunctionLiteral*, FunctionLiteralChange>> literal_stack;
605 for (
const SourcePositionEvent& event : events) {
606 switch (event.type) {
607 case SourcePositionEvent::DIFF_ENDS:
610 delta +=
event.pos_diff;
612 case SourcePositionEvent::LITERAL_ENDS: {
613 DCHECK_EQ(literal_stack.top().first,
event.literal);
614 FunctionLiteralChange& change = literal_stack.top().second;
615 change.new_end_position = inside_diff
617 :
event.literal->end_position() + delta;
618 result->insert(literal_stack.top());
622 case SourcePositionEvent::LITERAL_STARTS:
623 literal_stack.push(std::make_pair(
625 FunctionLiteralChange(
626 inside_diff ? kNoSourcePosition
627 : event.literal->start_position() + delta,
628 literal_stack.empty() ? nullptr : literal_stack.top().first)));
630 case SourcePositionEvent::DIFF_STARTS:
631 DCHECK(!inside_diff);
633 if (!literal_stack.empty()) {
638 literal_stack.top().second.has_changes =
true;
650 bool HasChangedScope(FunctionLiteral* a, FunctionLiteral* b) {
651 Scope* scope_a = a->scope()->outer_scope();
652 Scope* scope_b = b->scope()->outer_scope();
653 while (scope_a && scope_b) {
654 std::unordered_map<int, Handle<String>> vars;
655 for (Variable* var : *scope_a->locals()) {
656 if (!var->IsContextSlot())
continue;
657 vars[var->index()] = var->name();
659 for (Variable* var : *scope_b->locals()) {
660 if (!var->IsContextSlot())
continue;
661 auto it = vars.find(var->index());
662 if (it == vars.end())
return true;
663 if (*it->second != *var->name())
return true;
665 scope_a = scope_a->outer_scope();
666 scope_b = scope_b->outer_scope();
668 return scope_a != scope_b;
671 enum ChangeState { UNCHANGED, CHANGED, DAMAGED };
673 using LiteralMap = std::unordered_map<FunctionLiteral*, FunctionLiteral*>;
674 void MapLiterals(
const FunctionLiteralChanges& changes,
675 const std::vector<FunctionLiteral*>& new_literals,
676 LiteralMap* unchanged, LiteralMap* changed) {
679 const std::pair<int, int> kTopLevelMarker = std::make_pair(-1, -1);
680 std::map<std::pair<int, int>, FunctionLiteral*> position_to_new_literal;
681 for (FunctionLiteral* literal : new_literals) {
682 DCHECK(literal->start_position() != kNoSourcePosition);
683 DCHECK(literal->end_position() != kNoSourcePosition);
684 std::pair<int, int> key =
685 literal->function_literal_id() == FunctionLiteral::kIdTypeTopLevel
687 : std::make_pair(literal->start_position(),
688 literal->end_position());
690 DCHECK_EQ(position_to_new_literal.find(key), position_to_new_literal.end());
691 position_to_new_literal[key] = literal;
694 std::unordered_map<FunctionLiteral*, ChangeState> change_state;
695 for (
const auto& change_pair : changes) {
696 FunctionLiteral* literal = change_pair.first;
697 const FunctionLiteralChange& change = change_pair.second;
698 std::pair<int, int> key =
699 literal->function_literal_id() == FunctionLiteral::kIdTypeTopLevel
701 : std::make_pair(change.new_start_position,
702 change.new_end_position);
703 auto it = position_to_new_literal.find(key);
704 if (it == position_to_new_literal.end() ||
705 HasChangedScope(literal, it->second)) {
706 change_state[literal] = ChangeState::DAMAGED;
707 if (!change.outer_literal)
continue;
708 if (change_state[change.outer_literal] != ChangeState::DAMAGED) {
709 change_state[change.outer_literal] = ChangeState::CHANGED;
712 mappings[literal] = it->second;
713 if (change_state.find(literal) == change_state.end()) {
714 change_state[literal] =
715 change.has_changes ? ChangeState::CHANGED : ChangeState::UNCHANGED;
719 for (
const auto& mapping : mappings) {
720 if (change_state[mapping.first] == ChangeState::UNCHANGED) {
721 (*unchanged)[mapping.first] = mapping.second;
722 }
else if (change_state[mapping.first] == ChangeState::CHANGED) {
723 (*changed)[mapping.first] = mapping.second;
728 class CollectFunctionLiterals final
729 :
public AstTraversalVisitor<CollectFunctionLiterals> {
731 CollectFunctionLiterals(Isolate* isolate, AstNode* root)
732 : AstTraversalVisitor<CollectFunctionLiterals>(isolate, root) {}
733 void VisitFunctionLiteral(FunctionLiteral* lit) {
734 AstTraversalVisitor::VisitFunctionLiteral(lit);
735 literals_->push_back(lit);
737 void Run(std::vector<FunctionLiteral*>* literals) {
738 literals_ = literals;
739 AstTraversalVisitor::Run();
744 std::vector<FunctionLiteral*>* literals_;
747 bool ParseScript(Isolate* isolate, ParseInfo* parse_info,
bool compile_as_well,
748 std::vector<FunctionLiteral*>* literals,
749 debug::LiveEditResult* result) {
750 parse_info->set_eager();
751 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
752 Handle<SharedFunctionInfo> shared;
753 bool success =
false;
754 if (compile_as_well) {
756 Compiler::CompileForLiveEdit(parse_info, isolate).ToHandle(&shared);
758 success = parsing::ParseProgram(parse_info, isolate);
760 success = Compiler::Analyze(parse_info);
761 parse_info->ast_value_factory()->Internalize(isolate);
765 isolate->OptionalRescheduleException(
false);
766 DCHECK(try_catch.HasCaught());
767 result->message = try_catch.Message()->Get();
768 auto self = Utils::OpenHandle(*try_catch.Message());
770 result->line_number = msg->GetLineNumber();
771 result->column_number = msg->GetColumnNumber();
772 result->status = debug::LiveEditResult::COMPILE_ERROR;
775 CollectFunctionLiterals(isolate, parse_info->literal()).Run(literals);
779 struct FunctionData {
780 FunctionData(FunctionLiteral* literal,
bool should_restart)
782 stack_position(NOT_ON_STACK),
783 should_restart(should_restart) {}
785 FunctionLiteral* literal;
786 MaybeHandle<SharedFunctionInfo> shared;
787 std::vector<Handle<JSFunction>> js_functions;
788 std::vector<Handle<JSGeneratorObject>> running_generators;
796 BELOW_NON_DROPPABLE_FRAME,
799 StackPosition stack_position;
803 class FunctionDataMap :
public ThreadVisitor {
805 void AddInterestingLiteral(
int script_id, FunctionLiteral* literal,
806 bool should_restart) {
807 map_.emplace(GetFuncId(script_id, literal),
808 FunctionData{literal, should_restart});
811 bool Lookup(SharedFunctionInfo* sfi, FunctionData** data) {
812 int start_position = sfi->StartPosition();
813 if (!sfi->script()->IsScript() || start_position == -1) {
816 Script* script = Script::cast(sfi->script());
817 return Lookup(GetFuncId(script->id(), sfi), data);
820 bool Lookup(Handle<Script> script, FunctionLiteral* literal,
821 FunctionData** data) {
822 return Lookup(GetFuncId(script->id(), literal), data);
825 void Fill(Isolate* isolate, Address* restart_frame_fp) {
827 HeapIterator iterator(isolate->heap(), HeapIterator::kFilterUnreachable);
828 while (HeapObject* obj = iterator.next()) {
829 if (obj->IsSharedFunctionInfo()) {
830 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
831 FunctionData* data =
nullptr;
832 if (!Lookup(sfi, &data))
continue;
833 data->shared = handle(sfi, isolate);
834 }
else if (obj->IsJSFunction()) {
835 JSFunction* js_function = JSFunction::cast(obj);
836 SharedFunctionInfo* sfi = js_function->shared();
837 FunctionData* data =
nullptr;
838 if (!Lookup(sfi, &data))
continue;
839 data->js_functions.emplace_back(js_function, isolate);
840 }
else if (obj->IsJSGeneratorObject()) {
841 JSGeneratorObject* gen = JSGeneratorObject::cast(obj);
842 if (gen->is_closed())
continue;
843 SharedFunctionInfo* sfi = gen->function()->shared();
844 FunctionData* data =
nullptr;
845 if (!Lookup(sfi, &data))
continue;
846 data->running_generators.emplace_back(gen, isolate);
850 FunctionData::StackPosition stack_position =
851 isolate->debug()->break_frame_id() == StackFrame::NO_ID
852 ? FunctionData::PATCHABLE
853 : FunctionData::ABOVE_BREAK_FRAME;
854 for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
855 StackFrame* frame = it.frame();
856 if (stack_position == FunctionData::ABOVE_BREAK_FRAME) {
857 if (frame->id() == isolate->debug()->break_frame_id()) {
858 stack_position = FunctionData::PATCHABLE;
861 if (stack_position == FunctionData::PATCHABLE &&
862 (frame->is_exit() || frame->is_builtin_exit())) {
863 stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME;
866 if (!frame->is_java_script())
continue;
867 std::vector<Handle<SharedFunctionInfo>> sfis;
868 JavaScriptFrame::cast(frame)->GetFunctions(&sfis);
869 for (
auto& sfi : sfis) {
870 if (stack_position == FunctionData::PATCHABLE &&
871 IsResumableFunction(sfi->kind())) {
872 stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME;
874 FunctionData* data =
nullptr;
875 if (!Lookup(*sfi, &data))
continue;
876 if (!data->should_restart)
continue;
877 data->stack_position = stack_position;
878 *restart_frame_fp = frame->fp();
882 isolate->thread_manager()->IterateArchivedThreads(
this);
889 using FuncId = std::pair<int, int>;
891 FuncId GetFuncId(
int script_id, FunctionLiteral* literal) {
892 int start_position = literal->start_position();
893 if (literal->function_literal_id() == 0) {
896 DCHECK_EQ(start_position, 0);
899 return FuncId(script_id, start_position);
902 FuncId GetFuncId(
int script_id, SharedFunctionInfo* sfi) {
903 DCHECK_EQ(script_id, Script::cast(sfi->script())->
id());
904 int start_position = sfi->StartPosition();
905 DCHECK_NE(start_position, -1);
906 if (sfi->is_toplevel()) {
908 DCHECK_EQ(start_position, 0);
911 return FuncId(script_id, start_position);
914 bool Lookup(FuncId
id, FunctionData** data) {
915 auto it = map_.find(
id);
916 if (it == map_.end())
return false;
921 void VisitThread(Isolate* isolate, ThreadLocalTop* top)
override {
922 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
923 std::vector<Handle<SharedFunctionInfo>> sfis;
924 it.frame()->GetFunctions(&sfis);
925 for (
auto& sfi : sfis) {
926 FunctionData* data =
nullptr;
927 if (!Lookup(*sfi, &data))
continue;
928 data->stack_position = FunctionData::ARCHIVED_THREAD;
933 std::map<FuncId, FunctionData> map_;
936 bool CanPatchScript(
const LiteralMap& changed, Handle<Script> script,
937 Handle<Script> new_script,
938 FunctionDataMap& function_data_map,
939 debug::LiveEditResult* result) {
940 debug::LiveEditResult::Status status = debug::LiveEditResult::OK;
941 for (
const auto& mapping : changed) {
942 FunctionData* data =
nullptr;
943 function_data_map.Lookup(script, mapping.first, &data);
944 FunctionData* new_data =
nullptr;
945 function_data_map.Lookup(new_script, mapping.second, &new_data);
946 Handle<SharedFunctionInfo> sfi;
947 if (!data->shared.ToHandle(&sfi)) {
949 }
else if (!data->should_restart) {
951 }
else if (data->stack_position == FunctionData::ABOVE_BREAK_FRAME) {
952 status = debug::LiveEditResult::BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME;
953 }
else if (data->stack_position ==
954 FunctionData::BELOW_NON_DROPPABLE_FRAME) {
956 debug::LiveEditResult::BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME;
957 }
else if (!data->running_generators.empty()) {
958 status = debug::LiveEditResult::BLOCKED_BY_RUNNING_GENERATOR;
959 }
else if (data->stack_position == FunctionData::ARCHIVED_THREAD) {
960 status = debug::LiveEditResult::BLOCKED_BY_ACTIVE_FUNCTION;
962 if (status != debug::LiveEditResult::OK) {
963 result->status = status;
970 bool CanRestartFrame(Isolate* isolate, Address fp,
971 FunctionDataMap& function_data_map,
972 const LiteralMap& changed, debug::LiveEditResult* result) {
974 StackFrame* restart_frame =
nullptr;
975 StackFrameIterator it(isolate);
976 for (; !it.done(); it.Advance()) {
977 if (it.frame()->fp() == fp) {
978 restart_frame = it.frame();
982 DCHECK(restart_frame && restart_frame->is_java_script());
983 if (!LiveEdit::kFrameDropperSupported) {
984 result->status = debug::LiveEditResult::FRAME_RESTART_IS_NOT_SUPPORTED;
987 std::vector<Handle<SharedFunctionInfo>> sfis;
988 JavaScriptFrame::cast(restart_frame)->GetFunctions(&sfis);
989 for (
auto& sfi : sfis) {
990 FunctionData* data =
nullptr;
991 if (!function_data_map.Lookup(*sfi, &data))
continue;
992 auto new_literal_it = changed.find(data->literal);
993 if (new_literal_it == changed.end())
continue;
994 if (new_literal_it->second->scope()->new_target_var()) {
996 debug::LiveEditResult::BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME;
1003 void TranslateSourcePositionTable(Isolate* isolate, Handle<BytecodeArray> code,
1004 const std::vector<SourceChangeRange>& diffs) {
1005 SourcePositionTableBuilder builder;
1007 Handle<ByteArray> source_position_table(code->SourcePositionTable(), isolate);
1008 for (SourcePositionTableIterator iterator(*source_position_table);
1009 !iterator.done(); iterator.Advance()) {
1010 SourcePosition position = iterator.source_position();
1011 position.SetScriptOffset(
1012 LiveEdit::TranslatePosition(diffs, position.ScriptOffset()));
1013 builder.AddPosition(iterator.code_offset(), position,
1014 iterator.is_statement());
1017 Handle<ByteArray> new_source_position_table(
1018 builder.ToSourcePositionTable(isolate));
1019 code->set_source_position_table(*new_source_position_table);
1020 LOG_CODE_EVENT(isolate,
1021 CodeLinePosInfoRecordEvent(code->GetFirstBytecodeAddress(),
1022 *new_source_position_table));
1025 void UpdatePositions(Isolate* isolate, Handle<SharedFunctionInfo> sfi,
1026 const std::vector<SourceChangeRange>& diffs) {
1027 int old_start_position = sfi->StartPosition();
1028 int new_start_position =
1029 LiveEdit::TranslatePosition(diffs, old_start_position);
1030 int new_end_position = LiveEdit::TranslatePosition(diffs, sfi->EndPosition());
1031 int new_function_token_position =
1032 LiveEdit::TranslatePosition(diffs, sfi->function_token_position());
1033 sfi->SetPosition(new_start_position, new_end_position);
1034 sfi->SetFunctionTokenPosition(new_function_token_position,
1035 new_start_position);
1036 if (sfi->HasBytecodeArray()) {
1037 TranslateSourcePositionTable(
1038 isolate, handle(sfi->GetBytecodeArray(), isolate), diffs);
1043 void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script,
1044 Handle<String> new_source,
bool preview,
1045 debug::LiveEditResult* result) {
1046 std::vector<SourceChangeRange> diffs;
1047 LiveEdit::CompareStrings(isolate,
1048 handle(String::cast(script->source()), isolate),
1049 new_source, &diffs);
1050 if (diffs.empty()) {
1051 result->status = debug::LiveEditResult::OK;
1055 ParseInfo parse_info(isolate, script);
1056 std::vector<FunctionLiteral*> literals;
1057 if (!ParseScript(isolate, &parse_info,
false, &literals, result))
return;
1059 Handle<Script> new_script = isolate->factory()->CloneScript(script);
1060 new_script->set_source(*new_source);
1061 std::vector<FunctionLiteral*> new_literals;
1062 ParseInfo new_parse_info(isolate, new_script);
1063 if (!ParseScript(isolate, &new_parse_info,
true, &new_literals, result)) {
1067 FunctionLiteralChanges literal_changes;
1068 CalculateFunctionLiteralChanges(literals, diffs, &literal_changes);
1071 LiteralMap unchanged;
1072 MapLiterals(literal_changes, new_literals, &unchanged, &changed);
1074 FunctionDataMap function_data_map;
1075 for (
const auto& mapping : changed) {
1076 function_data_map.AddInterestingLiteral(script->id(), mapping.first,
true);
1077 function_data_map.AddInterestingLiteral(new_script->id(), mapping.second,
1080 for (
const auto& mapping : unchanged) {
1081 function_data_map.AddInterestingLiteral(script->id(), mapping.first,
false);
1083 Address restart_frame_fp = 0;
1084 function_data_map.Fill(isolate, &restart_frame_fp);
1086 if (!CanPatchScript(changed, script, new_script, function_data_map, result)) {
1089 if (restart_frame_fp &&
1090 !CanRestartFrame(isolate, restart_frame_fp, function_data_map, changed,
1096 result->status = debug::LiveEditResult::OK;
1100 std::map<int, int> start_position_to_unchanged_id;
1101 for (
const auto& mapping : unchanged) {
1102 FunctionData* data =
nullptr;
1103 if (!function_data_map.Lookup(script, mapping.first, &data))
continue;
1104 Handle<SharedFunctionInfo> sfi;
1105 if (!data->shared.ToHandle(&sfi))
continue;
1106 DCHECK_EQ(sfi->script(), *script);
1108 isolate->compilation_cache()->Remove(sfi);
1109 isolate->debug()->DeoptimizeFunction(sfi);
1110 if (sfi->HasDebugInfo()) {
1111 Handle<DebugInfo> debug_info(sfi->GetDebugInfo(), isolate);
1112 isolate->debug()->RemoveBreakInfoAndMaybeFree(debug_info);
1114 UpdatePositions(isolate, sfi, diffs);
1116 sfi->set_script(*new_script);
1117 if (sfi->HasUncompiledData()) {
1118 sfi->uncompiled_data()->set_function_literal_id(
1119 mapping.second->function_literal_id());
1121 new_script->shared_function_infos()->Set(
1122 mapping.second->function_literal_id(), HeapObjectReference::Weak(*sfi));
1123 DCHECK_EQ(sfi->FunctionLiteralId(isolate),
1124 mapping.second->function_literal_id());
1128 start_position_to_unchanged_id[mapping.second->start_position()] =
1129 mapping.second->function_literal_id();
1131 if (sfi->HasUncompiledDataWithPreParsedScope()) {
1132 sfi->ClearPreParsedScopeData();
1135 for (
auto& js_function : data->js_functions) {
1136 js_function->set_feedback_cell(*isolate->factory()->many_closures_cell());
1137 if (!js_function->is_compiled())
continue;
1138 JSFunction::EnsureFeedbackVector(js_function);
1141 if (!sfi->HasBytecodeArray())
continue;
1142 FixedArray constants = sfi->GetBytecodeArray()->constant_pool();
1143 for (
int i = 0;
i < constants->length(); ++
i) {
1144 if (!constants->get(
i)->IsSharedFunctionInfo())
continue;
1145 FunctionData* data =
nullptr;
1146 if (!function_data_map.Lookup(SharedFunctionInfo::cast(constants->get(
i)),
1150 auto change_it = changed.find(data->literal);
1151 if (change_it == changed.end())
continue;
1152 if (!function_data_map.Lookup(new_script, change_it->second, &data)) {
1155 Handle<SharedFunctionInfo> new_sfi;
1156 if (!data->shared.ToHandle(&new_sfi))
continue;
1157 constants->set(
i, *new_sfi);
1160 for (
const auto& mapping : changed) {
1161 FunctionData* data =
nullptr;
1162 if (!function_data_map.Lookup(new_script, mapping.second, &data))
continue;
1163 Handle<SharedFunctionInfo> new_sfi = data->shared.ToHandleChecked();
1164 DCHECK_EQ(new_sfi->script(), *new_script);
1166 if (!function_data_map.Lookup(script, mapping.first, &data))
continue;
1167 Handle<SharedFunctionInfo> sfi;
1168 if (!data->shared.ToHandle(&sfi))
continue;
1170 isolate->debug()->DeoptimizeFunction(sfi);
1171 isolate->compilation_cache()->Remove(sfi);
1172 for (
auto& js_function : data->js_functions) {
1173 js_function->set_shared(*new_sfi);
1174 js_function->set_code(js_function->shared()->GetCode());
1176 js_function->set_feedback_cell(*isolate->factory()->many_closures_cell());
1177 if (!js_function->is_compiled())
continue;
1178 JSFunction::EnsureFeedbackVector(js_function);
1181 SharedFunctionInfo::ScriptIterator it(isolate, *new_script);
1182 while (SharedFunctionInfo* sfi = it.Next()) {
1183 if (!sfi->HasBytecodeArray())
continue;
1184 FixedArray constants = sfi->GetBytecodeArray()->constant_pool();
1185 for (
int i = 0;
i < constants->length(); ++
i) {
1186 if (!constants->get(
i)->IsSharedFunctionInfo())
continue;
1187 SharedFunctionInfo* inner_sfi =
1188 SharedFunctionInfo::cast(constants->get(
i));
1192 start_position_to_unchanged_id.find(inner_sfi->StartPosition());
1193 if (unchanged_it == start_position_to_unchanged_id.end())
continue;
1197 SharedFunctionInfo* old_unchanged_inner_sfi =
1198 SharedFunctionInfo::cast(new_script->shared_function_infos()
1199 ->Get(unchanged_it->second)
1201 if (old_unchanged_inner_sfi == inner_sfi)
continue;
1202 DCHECK_NE(old_unchanged_inner_sfi, inner_sfi);
1205 DCHECK_EQ(old_unchanged_inner_sfi->script(), *new_script);
1206 constants->set(
i, old_unchanged_inner_sfi);
1214 DisallowHeapAllocation no_gc;
1216 SharedFunctionInfo::ScriptIterator it(isolate, *new_script);
1217 std::set<int> start_positions;
1218 while (SharedFunctionInfo* sfi = it.Next()) {
1219 DCHECK_EQ(sfi->script(), *new_script);
1220 DCHECK_EQ(sfi->FunctionLiteralId(isolate), it.CurrentIndex());
1223 if (sfi->is_toplevel()) {
1224 DCHECK_EQ(start_positions.find(sfi->StartPosition()),
1225 start_positions.end());
1226 start_positions.insert(sfi->StartPosition());
1229 if (!sfi->HasBytecodeArray())
continue;
1233 FixedArray constants = sfi->GetBytecodeArray()->constant_pool();
1234 for (
int i = 0;
i < constants->length(); ++
i) {
1235 if (!constants->get(
i)->IsSharedFunctionInfo())
continue;
1236 SharedFunctionInfo* inner_sfi =
1237 SharedFunctionInfo::cast(constants->get(
i));
1238 DCHECK_EQ(inner_sfi->script(), *new_script);
1239 DCHECK_EQ(inner_sfi, new_script->shared_function_infos()
1240 ->Get(inner_sfi->FunctionLiteralId(isolate))
1247 if (restart_frame_fp) {
1248 for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
1249 if (it.frame()->fp() == restart_frame_fp) {
1250 isolate->debug()->ScheduleFrameRestart(it.frame());
1251 result->stack_changed =
true;
1257 int script_id = script->id();
1258 script->set_id(new_script->id());
1259 new_script->set_id(script_id);
1260 result->status = debug::LiveEditResult::OK;
1261 result->script = ToApiHandle<v8::debug::Script>(new_script);
1264 void LiveEdit::InitializeThreadLocal(Debug* debug) {
1265 debug->thread_local_.restart_fp_ = 0;
1268 bool LiveEdit::RestartFrame(JavaScriptFrame* frame) {
1269 if (!LiveEdit::kFrameDropperSupported)
return false;
1270 Isolate* isolate = frame->isolate();
1271 StackFrame::Id break_frame_id = isolate->debug()->break_frame_id();
1272 bool break_frame_found = break_frame_id == StackFrame::NO_ID;
1273 for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
1274 StackFrame* current = it.frame();
1275 break_frame_found = break_frame_found || break_frame_id == current->id();
1276 if (current->fp() == frame->fp()) {
1277 if (break_frame_found) {
1278 isolate->debug()->ScheduleFrameRestart(current);
1284 if (!break_frame_found)
continue;
1285 if (current->is_exit() || current->is_builtin_exit()) {
1288 if (!current->is_java_script())
continue;
1289 std::vector<Handle<SharedFunctionInfo>> shareds;
1290 JavaScriptFrame::cast(current)->GetFunctions(&shareds);
1291 for (
auto& shared : shareds) {
1292 if (IsResumableFunction(shared->kind())) {
1300 void LiveEdit::CompareStrings(Isolate* isolate, Handle<String> s1,
1302 std::vector<SourceChangeRange>* diffs) {
1303 s1 = String::Flatten(isolate, s1);
1304 s2 = String::Flatten(isolate, s2);
1306 LineEndsWrapper line_ends1(isolate, s1);
1307 LineEndsWrapper line_ends2(isolate, s2);
1309 LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
1310 TokenizingLineArrayCompareOutput output(isolate, line_ends1, line_ends2, s1,
1313 NarrowDownInput(&input, &output);
1315 Comparator::CalculateDifference(&input, &output);
1318 int LiveEdit::TranslatePosition(
const std::vector<SourceChangeRange>& diffs,
1320 auto it = std::lower_bound(diffs.begin(), diffs.end(), position,
1321 [](
const SourceChangeRange& change,
int position) {
1322 return change.end_position < position;
1324 if (it != diffs.end() && position == it->end_position) {
1325 return it->new_end_position;
1327 if (it == diffs.begin())
return position;
1328 DCHECK(it == diffs.end() || position <= it->start_position);
1330 return position + (it->new_end_position - it->end_position);