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

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

Cr-Commit-Position: refs/heads/master@{#29296}
This commit is contained in:
yangguo 2015-06-25 07:27:36 -07:00 committed by Commit bot
parent 4eed4970fd
commit cfe89a71a3
2 changed files with 116 additions and 114 deletions

View File

@ -1984,126 +1984,125 @@ void Debug::PrepareForBreakPoints() {
}
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
int position) {
// Iterate the heap looking for SharedFunctionInfo generated from the
// script. The inner most SharedFunctionInfo containing the source position
// for the requested break point is found.
// NOTE: This might require several heap iterations. If the SharedFunctionInfo
// which is found is not compiled it is compiled and the heap is iterated
// again as the compilation might create inner functions from the newly
// compiled function and the actual requested break point might be in one of
// 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();
if (start_position == RelocInfo::kNoPosition) {
start_position = shared->start_position();
}
if (start_position <= position &&
position <= shared->end_position()) {
// If there is no candidate or this function is within the current
// candidate this is the new candidate.
if (target.is_null()) {
target_start_position = start_position;
target_function = function;
target = shared;
} else {
if (target_start_position == start_position &&
shared->end_position() == target->end_position()) {
// If a top-level function contains only one function
// declaration the source for the top-level and the function
// is the same. In that case prefer the non top-level function.
if (!shared->is_toplevel()) {
target_start_position = start_position;
target_function = function;
target = shared;
}
} 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;
}
}
}
}
} // End for loop.
} // End no-allocation scope.
class SharedFunctionInfoFinder {
public:
explicit SharedFunctionInfoFinder(int target_position)
: current_candidate_(NULL),
current_candidate_closure_(NULL),
current_start_position_(RelocInfo::kNoPosition),
target_position_(target_position) {}
if (target.is_null()) return isolate_->factory()->undefined_value();
// There will be at least one break point when we are done.
has_break_points_ = true;
// If the candidate found is compiled we are done.
done = target->is_compiled();
if (!done) {
// If the candidate is not compiled, compile it to reveal any inner
// functions which might contain the requested source position. This
// will compile all inner functions that cannot be compiled without a
// context, because Compiler::GetSharedFunctionInfo checks whether the
// debugger is active.
MaybeHandle<Code> maybe_result = target_function.is_null()
? Compiler::GetUnoptimizedCode(target)
: Compiler::GetUnoptimizedCode(target_function);
if (maybe_result.is_null()) return isolate_->factory()->undefined_value();
void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
int start_position = shared->function_token_position();
if (start_position == RelocInfo::kNoPosition) {
start_position = shared->start_position();
}
} // End while loop.
// JSFunctions from the same literal may not have the same shared function
// info. Find those JSFunctions and deduplicate the shared function info.
HeapIterator iterator(heap, FLAG_lazy ? HeapIterator::kNoFiltering
: HeapIterator::kFilterUnreachable);
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
if (!obj->IsJSFunction()) continue;
JSFunction* function = JSFunction::cast(obj);
SharedFunctionInfo* shared = function->shared();
if (shared != *target && shared->script() == target->script() &&
shared->start_position_and_type() ==
target->start_position_and_type()) {
function->set_shared(*target);
if (start_position > target_position_) return;
if (target_position_ > shared->end_position()) return;
if (current_candidate_ != NULL) {
if (current_start_position_ == start_position &&
shared->end_position() == current_candidate_->end_position()) {
// If a top-level function contains only one function
// declaration the source for the top-level and the function
// is the same. In that case prefer the non top-level function.
if (shared->is_toplevel()) return;
} else if (start_position < current_start_position_ ||
current_candidate_->end_position() < shared->end_position()) {
return;
}
}
current_start_position_ = start_position;
current_candidate_ = shared;
current_candidate_closure_ = closure;
}
return target;
SharedFunctionInfo* Result() { return current_candidate_; }
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;
Handle<C> compilable_handle(compilable);
bool result = !Compiler::GetUnoptimizedCode(compilable_handle).is_null();
has_break_points_ = has_break_points;
return result;
}
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
int position) {
while (true) {
// Go through all shared function infos associated with this script to
// find the inner most function containing this position.
if (!script->shared_function_infos()->IsWeakFixedArray()) break;
WeakFixedArray* array =
WeakFixedArray::cast(script->shared_function_infos());
SharedFunctionInfo* shared;
{
SharedFunctionInfoFinder finder(position);
for (int i = 0; i < array->Length(); i++) {
Object* item = array->Get(i);
if (!item->IsSharedFunctionInfo()) continue;
SharedFunctionInfo* shared = SharedFunctionInfo::cast(item);
finder.NewCandidate(shared);
}
shared = finder.Result();
if (shared == NULL) break;
// We found it if it's already compiled.
if (shared->is_compiled()) return handle(shared);
}
// If not, compile to reveal inner functions, if possible.
if (shared->allows_lazy_compilation_without_context()) {
if (!CompileToRevealInnerFunctions(shared)) break;
continue;
}
// 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();
}

View File

@ -481,6 +481,9 @@ class Debug {
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
template <typename C>
bool CompileToRevealInnerFunctions(C* compilable);
// This function is used in FunctionNameUsing* tests.
Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
int position);