Commit 058deb27 authored by yangguo's avatar yangguo Committed by Commit bot

Debugger: use list to find shared function info in a script.

Now that we keep tabs on shared function infos from a script, we can speed up finding shared function infos for debugging. However, in case we have to compile a function that cannot be lazily compiled without context, we fall back to the slow heap iteration.

R=mstarzinger@chromium.org
BUG=v8:4132,v8:4052
LOG=N

Committed: https://crrev.com/cfe89a71a332ef9ed481c8210bc3ad6d2822034b
Cr-Commit-Position: refs/heads/master@{#29296}

Review URL: https://codereview.chromium.org/1206573004

Cr-Commit-Position: refs/heads/master@{#29327}
parent 8c72792b
...@@ -1022,8 +1022,7 @@ void Compiler::CompileForLiveEdit(Handle<Script> script) { ...@@ -1022,8 +1022,7 @@ void Compiler::CompileForLiveEdit(Handle<Script> script) {
VMState<COMPILER> state(info.isolate()); VMState<COMPILER> state(info.isolate());
// Get rid of old list of shared function infos. // Get rid of old list of shared function infos.
script->set_shared_function_infos(Smi::FromInt(0)); info.MarkAsFirstCompile();
info.parse_info()->set_global(); info.parse_info()->set_global();
if (!Parser::ParseStatic(info.parse_info())) return; if (!Parser::ParseStatic(info.parse_info())) return;
...@@ -1346,11 +1345,14 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo( ...@@ -1346,11 +1345,14 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
FunctionLiteral* literal, Handle<Script> script, FunctionLiteral* literal, Handle<Script> script,
CompilationInfo* outer_info) { CompilationInfo* outer_info) {
// Precondition: code has been parsed and scopes have been analyzed. // Precondition: code has been parsed and scopes have been analyzed.
Isolate* isolate = outer_info->isolate();
MaybeHandle<SharedFunctionInfo> maybe_existing; MaybeHandle<SharedFunctionInfo> maybe_existing;
if (outer_info->is_first_compile()) { if (outer_info->is_first_compile()) {
// On the first compile, there are no existing shared function info for // On the first compile, there are no existing shared function info for
// inner functions yet, so do not try to find them. // inner functions yet, so do not try to find them. All bets are off for
DCHECK(script->FindSharedFunctionInfo(literal).is_null()); // live edit though.
DCHECK(script->FindSharedFunctionInfo(literal).is_null() ||
isolate->debug()->live_edit_enabled());
} else { } else {
maybe_existing = script->FindSharedFunctionInfo(literal); maybe_existing = script->FindSharedFunctionInfo(literal);
} }
...@@ -1371,8 +1373,6 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo( ...@@ -1371,8 +1373,6 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
if (outer_info->will_serialize()) info.PrepareForSerializing(); if (outer_info->will_serialize()) info.PrepareForSerializing();
if (outer_info->is_first_compile()) info.MarkAsFirstCompile(); if (outer_info->is_first_compile()) info.MarkAsFirstCompile();
Isolate* isolate = info.isolate();
Factory* factory = isolate->factory();
LiveEditFunctionTracker live_edit_tracker(isolate, literal); LiveEditFunctionTracker live_edit_tracker(isolate, literal);
// Determine if the function can be lazily compiled. This is necessary to // Determine if the function can be lazily compiled. This is necessary to
// allow some of our builtin JS files to be lazily compiled. These // allow some of our builtin JS files to be lazily compiled. These
...@@ -1427,9 +1427,10 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo( ...@@ -1427,9 +1427,10 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
if (maybe_existing.is_null()) { if (maybe_existing.is_null()) {
// Create a shared function info object. // Create a shared function info object.
Handle<SharedFunctionInfo> result = factory->NewSharedFunctionInfo( Handle<SharedFunctionInfo> result =
literal->name(), literal->materialized_literal_count(), literal->kind(), isolate->factory()->NewSharedFunctionInfo(
info.code(), scope_info, info.feedback_vector()); literal->name(), literal->materialized_literal_count(),
literal->kind(), info.code(), scope_info, info.feedback_vector());
SharedFunctionInfo::InitFromFunctionLiteral(result, literal); SharedFunctionInfo::InitFromFunctionLiteral(result, literal);
SharedFunctionInfo::SetScript(result, script); SharedFunctionInfo::SetScript(result, script);
......
...@@ -1984,126 +1984,125 @@ void Debug::PrepareForBreakPoints() { ...@@ -1984,126 +1984,125 @@ void Debug::PrepareForBreakPoints() {
} }
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, class SharedFunctionInfoFinder {
int position) { public:
// Iterate the heap looking for SharedFunctionInfo generated from the explicit SharedFunctionInfoFinder(int target_position)
// script. The inner most SharedFunctionInfo containing the source position : current_candidate_(NULL),
// for the requested break point is found. current_candidate_closure_(NULL),
// NOTE: This might require several heap iterations. If the SharedFunctionInfo current_start_position_(RelocInfo::kNoPosition),
// which is found is not compiled it is compiled and the heap is iterated target_position_(target_position) {}
// again as the compilation might create inner functions from the newly
// compiled function and the actual requested break point might be in one of void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
// these functions.
// NOTE: The below fix-point iteration depends on all functions that cannot be
// compiled lazily without a context to not be compiled at all. Compilation
// will be triggered at points where we do not need a context.
bool done = false;
// The current candidate for the source position:
int target_start_position = RelocInfo::kNoPosition;
Handle<JSFunction> target_function;
Handle<SharedFunctionInfo> target;
Heap* heap = isolate_->heap();
while (!done) {
{ // Extra scope for iterator.
// If lazy compilation is off, we won't have duplicate shared function
// infos that need to be filtered.
HeapIterator iterator(heap, FLAG_lazy ? HeapIterator::kNoFiltering
: HeapIterator::kFilterUnreachable);
for (HeapObject* obj = iterator.next();
obj != NULL; obj = iterator.next()) {
bool found_next_candidate = false;
Handle<JSFunction> function;
Handle<SharedFunctionInfo> shared;
if (obj->IsJSFunction()) {
function = Handle<JSFunction>(JSFunction::cast(obj));
shared = Handle<SharedFunctionInfo>(function->shared());
DCHECK(shared->allows_lazy_compilation() || shared->is_compiled());
found_next_candidate = true;
} else if (obj->IsSharedFunctionInfo()) {
shared = Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj));
// Skip functions that we cannot compile lazily without a context,
// which is not available here, because there is no closure.
found_next_candidate = shared->is_compiled() ||
shared->allows_lazy_compilation_without_context();
}
if (!found_next_candidate) continue;
if (shared->script() == *script) {
// If the SharedFunctionInfo found has the requested script data and
// contains the source position it is a candidate.
int start_position = shared->function_token_position(); int start_position = shared->function_token_position();
if (start_position == RelocInfo::kNoPosition) { if (start_position == RelocInfo::kNoPosition) {
start_position = shared->start_position(); start_position = shared->start_position();
} }
if (start_position <= position &&
position <= shared->end_position()) { if (start_position > target_position_) return;
// If there is no candidate or this function is within the current if (target_position_ > shared->end_position()) return;
// candidate this is the new candidate.
if (target.is_null()) { if (current_candidate_ != NULL) {
target_start_position = start_position; if (current_start_position_ == start_position &&
target_function = function; shared->end_position() == current_candidate_->end_position()) {
target = shared;
} else {
if (target_start_position == start_position &&
shared->end_position() == target->end_position()) {
// If a top-level function contains only one function // If a top-level function contains only one function
// declaration the source for the top-level and the function // declaration the source for the top-level and the function
// is the same. In that case prefer the non top-level function. // is the same. In that case prefer the non top-level function.
if (!shared->is_toplevel()) { if (shared->is_toplevel()) return;
target_start_position = start_position; } else if (start_position < current_start_position_ ||
target_function = function; current_candidate_->end_position() < shared->end_position()) {
target = shared; return;
}
} else if (target_start_position <= start_position &&
shared->end_position() <= target->end_position()) {
// This containment check includes equality as a function
// inside a top-level function can share either start or end
// position with the top-level function.
target_start_position = start_position;
target_function = function;
target = shared;
}
} }
} }
current_start_position_ = start_position;
current_candidate_ = shared;
current_candidate_closure_ = closure;
} }
} // End for loop.
} // End no-allocation scope.
if (target.is_null()) return isolate_->factory()->undefined_value(); SharedFunctionInfo* Result() { return current_candidate_; }
// There will be at least one break point when we are done. JSFunction* ResultClosure() { return current_candidate_closure_; }
private:
SharedFunctionInfo* current_candidate_;
JSFunction* current_candidate_closure_;
int current_start_position_;
int target_position_;
DisallowHeapAllocation no_gc_;
};
template <typename C>
bool Debug::CompileToRevealInnerFunctions(C* compilable) {
HandleScope scope(isolate_);
// Force compiling inner functions that require context.
// TODO(yangguo): remove this hack.
bool has_break_points = has_break_points_;
has_break_points_ = true; has_break_points_ = true;
Handle<C> compilable_handle(compilable);
bool result = !Compiler::GetUnoptimizedCode(compilable_handle).is_null();
has_break_points_ = has_break_points;
return result;
}
// If the candidate found is compiled we are done.
done = target->is_compiled(); Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
if (!done) { int position) {
// If the candidate is not compiled, compile it to reveal any inner while (true) {
// functions which might contain the requested source position. This // Go through all shared function infos associated with this script to
// will compile all inner functions that cannot be compiled without a // find the inner most function containing this position.
// context, because Compiler::GetSharedFunctionInfo checks whether the if (!script->shared_function_infos()->IsWeakFixedArray()) break;
// debugger is active. WeakFixedArray* array =
MaybeHandle<Code> maybe_result = target_function.is_null() WeakFixedArray::cast(script->shared_function_infos());
? Compiler::GetUnoptimizedCode(target)
: Compiler::GetUnoptimizedCode(target_function); SharedFunctionInfo* shared;
if (maybe_result.is_null()) return isolate_->factory()->undefined_value(); {
} SharedFunctionInfoFinder finder(position);
} // End while loop. for (int i = 0; i < array->Length(); i++) {
Object* item = array->Get(i);
// JSFunctions from the same literal may not have the same shared function if (!item->IsSharedFunctionInfo()) continue;
// info. Find those JSFunctions and deduplicate the shared function info. SharedFunctionInfo* shared = SharedFunctionInfo::cast(item);
HeapIterator iterator(heap, FLAG_lazy ? HeapIterator::kNoFiltering finder.NewCandidate(shared);
: HeapIterator::kFilterUnreachable); }
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { shared = finder.Result();
if (!obj->IsJSFunction()) continue; if (shared == NULL) break;
JSFunction* function = JSFunction::cast(obj); // We found it if it's already compiled.
SharedFunctionInfo* shared = function->shared(); if (shared->is_compiled()) return handle(shared);
if (shared != *target && shared->script() == target->script() && }
shared->start_position_and_type() == // If not, compile to reveal inner functions, if possible.
target->start_position_and_type()) { if (shared->allows_lazy_compilation_without_context()) {
function->set_shared(*target); if (!CompileToRevealInnerFunctions(shared)) break;
} continue;
} }
return target; // If not possible, comb the heap for the best suitable compile target.
JSFunction* closure;
{
HeapIterator it(isolate_->heap(), HeapIterator::kNoFiltering);
SharedFunctionInfoFinder finder(position);
while (HeapObject* object = it.next()) {
JSFunction* closure = NULL;
SharedFunctionInfo* shared = NULL;
if (object->IsJSFunction()) {
closure = JSFunction::cast(object);
shared = closure->shared();
} else if (object->IsSharedFunctionInfo()) {
shared = SharedFunctionInfo::cast(object);
if (!shared->allows_lazy_compilation_without_context()) continue;
} else {
continue;
}
if (shared->script() == *script) finder.NewCandidate(shared, closure);
}
closure = finder.ResultClosure();
shared = finder.Result();
}
if (closure == NULL ? !CompileToRevealInnerFunctions(shared)
: !CompileToRevealInnerFunctions(closure)) {
break;
}
}
return isolate_->factory()->undefined_value();
} }
......
...@@ -481,6 +481,9 @@ class Debug { ...@@ -481,6 +481,9 @@ class Debug {
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared); static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared); static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
template <typename C>
bool CompileToRevealInnerFunctions(C* compilable);
// This function is used in FunctionNameUsing* tests. // This function is used in FunctionNameUsing* tests.
Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script, Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
int position); int position);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment