diff options
Diffstat (limited to 'deps/v8/src/debug/liveedit.cc')
-rw-r--r-- | deps/v8/src/debug/liveedit.cc | 197 |
1 files changed, 119 insertions, 78 deletions
diff --git a/deps/v8/src/debug/liveedit.cc b/deps/v8/src/debug/liveedit.cc index 7193d0abd1..371d1d5575 100644 --- a/deps/v8/src/debug/liveedit.cc +++ b/deps/v8/src/debug/liveedit.cc @@ -4,7 +4,7 @@ #include "src/debug/liveedit.h" -#include "src/api.h" +#include "src/api-inl.h" #include "src/ast/ast-traversal-visitor.h" #include "src/ast/ast.h" #include "src/ast/scopes.h" @@ -17,6 +17,7 @@ #include "src/messages.h" #include "src/objects-inl.h" #include "src/objects/hash-table-inl.h" +#include "src/objects/js-generator-inl.h" #include "src/parsing/parse-info.h" #include "src/parsing/parsing.h" #include "src/source-position-table.h" @@ -542,9 +543,25 @@ struct SourcePositionEvent { if (a.position != b.position) return a.position < b.position; if (a.type != b.type) return a.type < b.type; if (a.type == LITERAL_STARTS && b.type == LITERAL_STARTS) { - return a.literal->end_position() < b.literal->end_position(); + // If the literals start in the same position, we want the one with the + // furthest (i.e. largest) end position to be first. + if (a.literal->end_position() != b.literal->end_position()) { + return a.literal->end_position() > b.literal->end_position(); + } + // If they also end in the same position, we want the first in order of + // literal ids to be first. + return a.literal->function_literal_id() < + b.literal->function_literal_id(); } else if (a.type == LITERAL_ENDS && b.type == LITERAL_ENDS) { - return a.literal->start_position() > b.literal->start_position(); + // If the literals end in the same position, we want the one with the + // nearest (i.e. largest) start position to be first. + if (a.literal->start_position() != b.literal->start_position()) { + return a.literal->start_position() > b.literal->start_position(); + } + // If they also end in the same position, we want the last in order of + // literal ids to be first. + return a.literal->function_literal_id() > + b.literal->function_literal_id(); } else { return a.pos_diff < b.pos_diff; } @@ -658,20 +675,33 @@ using LiteralMap = std::unordered_map<FunctionLiteral*, FunctionLiteral*>; void MapLiterals(const FunctionLiteralChanges& changes, const std::vector<FunctionLiteral*>& new_literals, LiteralMap* unchanged, LiteralMap* changed) { + // Track the top-level script function separately as it can overlap fully with + // another function, e.g. the script "()=>42". + const std::pair<int, int> kTopLevelMarker = std::make_pair(-1, -1); std::map<std::pair<int, int>, FunctionLiteral*> position_to_new_literal; for (FunctionLiteral* literal : new_literals) { DCHECK(literal->start_position() != kNoSourcePosition); DCHECK(literal->end_position() != kNoSourcePosition); - position_to_new_literal[std::make_pair(literal->start_position(), - literal->end_position())] = literal; + std::pair<int, int> key = + literal->function_literal_id() == FunctionLiteral::kIdTypeTopLevel + ? kTopLevelMarker + : std::make_pair(literal->start_position(), + literal->end_position()); + // Make sure there are no duplicate keys. + DCHECK_EQ(position_to_new_literal.find(key), position_to_new_literal.end()); + position_to_new_literal[key] = literal; } LiteralMap mappings; std::unordered_map<FunctionLiteral*, ChangeState> change_state; for (const auto& change_pair : changes) { FunctionLiteral* literal = change_pair.first; const FunctionLiteralChange& change = change_pair.second; - auto it = position_to_new_literal.find( - std::make_pair(change.new_start_position, change.new_end_position)); + std::pair<int, int> key = + literal->function_literal_id() == FunctionLiteral::kIdTypeTopLevel + ? kTopLevelMarker + : std::make_pair(change.new_start_position, + change.new_end_position); + auto it = position_to_new_literal.find(key); if (it == position_to_new_literal.end() || HasChangedScope(literal, it->second)) { change_state[literal] = ChangeState::DAMAGED; @@ -775,22 +805,22 @@ class FunctionDataMap : public ThreadVisitor { public: void AddInterestingLiteral(int script_id, FunctionLiteral* literal, bool should_restart) { - map_.emplace(std::make_pair(script_id, literal->function_literal_id()), + map_.emplace(GetFuncId(script_id, literal), FunctionData{literal, should_restart}); } - bool Lookup(Isolate* isolate, SharedFunctionInfo* sfi, FunctionData** data) { - int function_literal_id = sfi->FunctionLiteralId(isolate); - if (!sfi->script()->IsScript() || function_literal_id == -1) { + bool Lookup(SharedFunctionInfo* sfi, FunctionData** data) { + int start_position = sfi->StartPosition(); + if (!sfi->script()->IsScript() || start_position == -1) { return false; } Script* script = Script::cast(sfi->script()); - return Lookup(script->id(), function_literal_id, data); + return Lookup(GetFuncId(script->id(), sfi), data); } bool Lookup(Handle<Script> script, FunctionLiteral* literal, FunctionData** data) { - return Lookup(script->id(), literal->function_literal_id(), data); + return Lookup(GetFuncId(script->id(), literal), data); } void Fill(Isolate* isolate, Address* restart_frame_fp) { @@ -800,20 +830,20 @@ class FunctionDataMap : public ThreadVisitor { if (obj->IsSharedFunctionInfo()) { SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); FunctionData* data = nullptr; - if (!Lookup(isolate, sfi, &data)) continue; + if (!Lookup(sfi, &data)) continue; data->shared = handle(sfi, isolate); } else if (obj->IsJSFunction()) { JSFunction* js_function = JSFunction::cast(obj); SharedFunctionInfo* sfi = js_function->shared(); FunctionData* data = nullptr; - if (!Lookup(isolate, sfi, &data)) continue; + if (!Lookup(sfi, &data)) continue; data->js_functions.emplace_back(js_function, isolate); } else if (obj->IsJSGeneratorObject()) { JSGeneratorObject* gen = JSGeneratorObject::cast(obj); if (gen->is_closed()) continue; SharedFunctionInfo* sfi = gen->function()->shared(); FunctionData* data = nullptr; - if (!Lookup(isolate, sfi, &data)) continue; + if (!Lookup(sfi, &data)) continue; data->running_generators.emplace_back(gen, isolate); } } @@ -843,7 +873,7 @@ class FunctionDataMap : public ThreadVisitor { stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME; } FunctionData* data = nullptr; - if (!Lookup(isolate, *sfi, &data)) continue; + if (!Lookup(*sfi, &data)) continue; if (!data->should_restart) continue; data->stack_position = stack_position; *restart_frame_fp = frame->fp(); @@ -854,8 +884,36 @@ class FunctionDataMap : public ThreadVisitor { } private: - bool Lookup(int script_id, int function_literal_id, FunctionData** data) { - auto it = map_.find(std::make_pair(script_id, function_literal_id)); + // Unique id for a function: script_id + start_position, where start_position + // is special cased to -1 for top-level so that it does not overlap with a + // function whose start position is 0. + using FuncId = std::pair<int, int>; + + FuncId GetFuncId(int script_id, FunctionLiteral* literal) { + int start_position = literal->start_position(); + if (literal->function_literal_id() == 0) { + // This is the top-level script function literal, so special case its + // start position + DCHECK_EQ(start_position, 0); + start_position = -1; + } + return FuncId(script_id, start_position); + } + + FuncId GetFuncId(int script_id, SharedFunctionInfo* sfi) { + DCHECK_EQ(script_id, Script::cast(sfi->script())->id()); + int start_position = sfi->StartPosition(); + DCHECK_NE(start_position, -1); + if (sfi->is_toplevel()) { + // This is the top-level function, so special case its start position + DCHECK_EQ(start_position, 0); + start_position = -1; + } + return FuncId(script_id, start_position); + } + + bool Lookup(FuncId id, FunctionData** data) { + auto it = map_.find(id); if (it == map_.end()) return false; *data = &it->second; return true; @@ -867,14 +925,13 @@ class FunctionDataMap : public ThreadVisitor { it.frame()->GetFunctions(&sfis); for (auto& sfi : sfis) { FunctionData* data = nullptr; - if (!Lookup(isolate, *sfi, &data)) continue; + if (!Lookup(*sfi, &data)) continue; data->stack_position = FunctionData::ARCHIVED_THREAD; } } } - using UniqueLiteralId = std::pair<int, int>; // script_id + literal_id - std::map<UniqueLiteralId, FunctionData> map_; + std::map<FuncId, FunctionData> map_; }; bool CanPatchScript(const LiteralMap& changed, Handle<Script> script, @@ -932,7 +989,7 @@ bool CanRestartFrame(Isolate* isolate, Address fp, JavaScriptFrame::cast(restart_frame)->GetFunctions(&sfis); for (auto& sfi : sfis) { FunctionData* data = nullptr; - if (!function_data_map.Lookup(isolate, *sfi, &data)) continue; + if (!function_data_map.Lookup(*sfi, &data)) continue; auto new_literal_it = changed.find(data->literal); if (new_literal_it == changed.end()) continue; if (new_literal_it->second->scope()->new_target_var()) { @@ -944,9 +1001,8 @@ bool CanRestartFrame(Isolate* isolate, Address fp, return true; } -void TranslateSourcePositionTable(Handle<BytecodeArray> code, +void TranslateSourcePositionTable(Isolate* isolate, Handle<BytecodeArray> code, const std::vector<SourceChangeRange>& diffs) { - Isolate* isolate = code->GetIsolate(); SourcePositionTableBuilder builder; Handle<ByteArray> source_position_table(code->SourcePositionTable(), isolate); @@ -979,8 +1035,8 @@ void UpdatePositions(Isolate* isolate, Handle<SharedFunctionInfo> sfi, sfi->SetFunctionTokenPosition(new_function_token_position, new_start_position); if (sfi->HasBytecodeArray()) { - TranslateSourcePositionTable(handle(sfi->GetBytecodeArray(), isolate), - diffs); + TranslateSourcePositionTable( + isolate, handle(sfi->GetBytecodeArray(), isolate), diffs); } } } // anonymous namespace @@ -1042,6 +1098,7 @@ void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script, return; } + std::map<int, int> start_position_to_unchanged_id; for (const auto& mapping : unchanged) { FunctionData* data = nullptr; if (!function_data_map.Lookup(script, mapping.first, &data)) continue; @@ -1057,10 +1114,6 @@ void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script, } UpdatePositions(isolate, sfi, diffs); - MaybeObject* weak_redundant_new_sfi = - new_script->shared_function_infos()->Get( - mapping.second->function_literal_id()); - sfi->set_script(*new_script); if (sfi->HasUncompiledData()) { sfi->uncompiled_data()->set_function_literal_id( @@ -1071,26 +1124,10 @@ void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script, DCHECK_EQ(sfi->FunctionLiteralId(isolate), mapping.second->function_literal_id()); - // Swap the now-redundant, newly compiled SFI into the old script, so that - // we can look up the old function_literal_id using the new SFI when - // processing changed functions. - HeapObject* redundant_new_sfi_obj; - if (weak_redundant_new_sfi->ToStrongOrWeakHeapObject( - &redundant_new_sfi_obj)) { - SharedFunctionInfo* redundant_new_sfi = - SharedFunctionInfo::cast(redundant_new_sfi_obj); - - redundant_new_sfi->set_script(*script); - if (redundant_new_sfi->HasUncompiledData()) { - redundant_new_sfi->uncompiled_data()->set_function_literal_id( - mapping.first->function_literal_id()); - } - script->shared_function_infos()->Set( - mapping.first->function_literal_id(), - HeapObjectReference::Weak(redundant_new_sfi)); - DCHECK_EQ(redundant_new_sfi->FunctionLiteralId(isolate), - mapping.first->function_literal_id()); - } + // Save the new start_position -> id mapping, so that we can recover it when + // iterating over changed functions' constant pools. + start_position_to_unchanged_id[mapping.second->start_position()] = + mapping.second->function_literal_id(); if (sfi->HasUncompiledDataWithPreParsedScope()) { sfi->ClearPreParsedScopeData(); @@ -1107,8 +1144,8 @@ void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script, for (int i = 0; i < constants->length(); ++i) { if (!constants->get(i)->IsSharedFunctionInfo()) continue; FunctionData* data = nullptr; - if (!function_data_map.Lookup( - isolate, SharedFunctionInfo::cast(constants->get(i)), &data)) { + if (!function_data_map.Lookup(SharedFunctionInfo::cast(constants->get(i)), + &data)) { continue; } auto change_it = changed.find(data->literal); @@ -1148,44 +1185,48 @@ void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script, if (!constants->get(i)->IsSharedFunctionInfo()) continue; SharedFunctionInfo* inner_sfi = SharedFunctionInfo::cast(constants->get(i)); - if (inner_sfi->script() != *script) continue; - - // If the inner SFI's script is the old script, then this is actually a - // redundant new_script SFI where the old script SFI was unchanged, so we - // swapped their scripts in the unchanged iteration. This means that we - // have to update this changed SFI's inner SFI constant to point at the - // old inner SFI, which has already been patched to be on the new script. - // - // So, we look up FunctionData using the current, newly compiled - // inner_sfi, but the resulting FunctionData will still be referring to - // the old, unchanged SFI. - FunctionData* data = nullptr; - if (!function_data_map.Lookup(isolate, inner_sfi, &data)) continue; - Handle<SharedFunctionInfo> old_unchanged_inner_sfi = - data->shared.ToHandleChecked(); + + // See if there is a mapping from this function's start position to a + // unchanged function's id. + auto unchanged_it = + start_position_to_unchanged_id.find(inner_sfi->StartPosition()); + if (unchanged_it == start_position_to_unchanged_id.end()) continue; + + // Grab that function id from the new script's SFI list, which should have + // already been updated in in the unchanged pass. + SharedFunctionInfo* old_unchanged_inner_sfi = + SharedFunctionInfo::cast(new_script->shared_function_infos() + ->Get(unchanged_it->second) + ->GetHeapObject()); // Now some sanity checks. Make sure that this inner_sfi is not the // unchanged SFI yet... - DCHECK_NE(*old_unchanged_inner_sfi, inner_sfi); - // ... that the unchanged SFI has already been processed and patched to be - // on the new script ... + DCHECK_NE(old_unchanged_inner_sfi, inner_sfi); + // ... and that the unchanged SFI has already been processed and patched + // to be on the new script ... DCHECK_EQ(old_unchanged_inner_sfi->script(), *new_script); - // ... and that the id of the unchanged SFI matches the unchanged target - // literal's id. - DCHECK_EQ(old_unchanged_inner_sfi->FunctionLiteralId(isolate), - unchanged[data->literal]->function_literal_id()); - constants->set(i, *old_unchanged_inner_sfi); + + constants->set(i, old_unchanged_inner_sfi); } } #ifdef DEBUG { - // Check that all the functions in the new script are valid and that their - // function literals match what is expected. + // Check that all the functions in the new script are valid, that their + // function literals match what is expected, and that start positions are + // unique. DisallowHeapAllocation no_gc; SharedFunctionInfo::ScriptIterator it(isolate, *new_script); + std::set<int> start_positions; while (SharedFunctionInfo* sfi = it.Next()) { DCHECK_EQ(sfi->script(), *new_script); DCHECK_EQ(sfi->FunctionLiteralId(isolate), it.CurrentIndex()); + // Don't check the start position of the top-level function, as it can + // overlap with a function in the script. + if (sfi->is_toplevel()) { + DCHECK_EQ(start_positions.find(sfi->StartPosition()), + start_positions.end()); + start_positions.insert(sfi->StartPosition()); + } if (!sfi->HasBytecodeArray()) continue; // Check that all the functions in this function's constant pool are also |