summaryrefslogtreecommitdiff
path: root/deps/v8/src/debug/liveedit.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/debug/liveedit.cc')
-rw-r--r--deps/v8/src/debug/liveedit.cc197
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