[debugger] Re-compile top level functions for SharedFunctionInfos

This adds a call to re-compile top level functions in case
no shared function info could be found. We ran into a bug
where it was not possible to set a breakpoint on the
top-level function since it was already removed by the GC.

Bug: chromium:1137141
Change-Id: I5bb6984825eee8ebcb44619e15b3acf3d118b9bb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2672181
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Kim-Anh Tran <kimanh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72738}
This commit is contained in:
Kim-Anh Tran 2021-02-15 07:39:26 +01:00 committed by Commit Bot
parent 04441c4407
commit 312a3fbec0
5 changed files with 104 additions and 21 deletions

View File

@ -1422,14 +1422,6 @@ void CompileOnBackgroundThread(ParseInfo* parse_info,
// Character stream shouldn't be used again.
parse_info->ResetCharacterStream();
}
MaybeHandle<SharedFunctionInfo> CompileToplevel(
ParseInfo* parse_info, Handle<Script> script, Isolate* isolate,
IsCompiledScope* is_compiled_scope) {
return CompileToplevel(parse_info, script, kNullMaybeHandle, isolate,
is_compiled_scope);
}
} // namespace
CompilationHandleScope::~CompilationHandleScope() {
@ -1897,6 +1889,14 @@ bool Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag flag,
return true;
}
// static
MaybeHandle<SharedFunctionInfo> Compiler::CompileToplevel(
ParseInfo* parse_info, Handle<Script> script, Isolate* isolate,
IsCompiledScope* is_compiled_scope) {
return v8::internal::CompileToplevel(parse_info, script, kNullMaybeHandle,
isolate, is_compiled_scope);
}
// static
bool Compiler::FinalizeBackgroundCompileTask(
BackgroundCompileTask* task, Handle<SharedFunctionInfo> shared_info,
@ -1983,7 +1983,8 @@ bool Compiler::CompileOptimized(Handle<JSFunction> function,
MaybeHandle<SharedFunctionInfo> Compiler::CompileForLiveEdit(
ParseInfo* parse_info, Handle<Script> script, Isolate* isolate) {
IsCompiledScope is_compiled_scope;
return CompileToplevel(parse_info, script, isolate, &is_compiled_scope);
return Compiler::CompileToplevel(parse_info, script, isolate,
&is_compiled_scope);
}
// static
@ -2065,8 +2066,9 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
}
script->set_eval_from_position(eval_position);
if (!CompileToplevel(&parse_info, script, maybe_outer_scope_info, isolate,
&is_compiled_scope)
if (!v8::internal::CompileToplevel(&parse_info, script,
maybe_outer_scope_info, isolate,
&is_compiled_scope)
.ToHandle(&shared_info)) {
return MaybeHandle<JSFunction>();
}
@ -2538,7 +2540,8 @@ MaybeHandle<SharedFunctionInfo> CompileScriptOnMainThread(
script->IsUserJavaScript());
DCHECK_EQ(parse_info.flags().is_repl_mode(), script->is_repl_mode());
return CompileToplevel(&parse_info, script, isolate, is_compiled_scope);
return Compiler::CompileToplevel(&parse_info, script, isolate,
is_compiled_scope);
}
class StressBackgroundCompileThread : public base::Thread {
@ -2853,8 +2856,9 @@ MaybeHandle<JSFunction> Compiler::GetWrappedFunction(
origin_options, NOT_NATIVES_CODE, arguments);
Handle<SharedFunctionInfo> top_level;
maybe_result = CompileToplevel(&parse_info, script, maybe_outer_scope_info,
isolate, &is_compiled_scope);
maybe_result = v8::internal::CompileToplevel(&parse_info, script,
maybe_outer_scope_info,
isolate, &is_compiled_scope);
if (maybe_result.is_null()) isolate->ReportPendingMessages();
ASSIGN_RETURN_ON_EXCEPTION(isolate, top_level, maybe_result, JSFunction);

View File

@ -72,6 +72,9 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic {
IsCompiledScope* is_compiled_scope);
static bool CompileOptimized(Handle<JSFunction> function,
ConcurrencyMode mode, CodeKind code_kind);
static MaybeHandle<SharedFunctionInfo> CompileToplevel(
ParseInfo* parse_info, Handle<Script> script, Isolate* isolate,
IsCompiledScope* is_compiled_scope);
static void LogFunctionCompilation(Isolate* isolate,
CodeEventListener::LogEventsAndTags tag,

View File

@ -1608,6 +1608,19 @@ class SharedFunctionInfoFinder {
DISALLOW_GARBAGE_COLLECTION(no_gc_)
};
namespace {
SharedFunctionInfo FindSharedFunctionInfoCandidate(int position,
Handle<Script> script,
Isolate* isolate) {
SharedFunctionInfoFinder finder(position);
SharedFunctionInfo::ScriptIterator iterator(isolate, *script);
for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
info = iterator.Next()) {
finder.NewCandidate(info);
}
return finder.Result();
}
} // namespace
// We need to find a SFI for a literal that may not yet have been compiled yet,
// and there may not be a JSFunction referencing it. Find the SFI closest to
@ -1626,14 +1639,20 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
SharedFunctionInfo shared;
IsCompiledScope is_compiled_scope;
{
SharedFunctionInfoFinder finder(position);
SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
info = iterator.Next()) {
finder.NewCandidate(info);
shared = FindSharedFunctionInfoCandidate(position, script, isolate_);
if (shared.is_null()) {
// It might be that the shared function info is not available as the
// top level functions are removed due to the GC. Try to recompile
// the top level functions.
UnoptimizedCompileState compile_state(isolate_);
UnoptimizedCompileFlags flags =
UnoptimizedCompileFlags::ForScriptCompile(isolate_, *script);
ParseInfo parse_info(isolate_, flags, &compile_state);
IsCompiledScope is_compiled_scope;
Compiler::CompileToplevel(&parse_info, script, isolate_,
&is_compiled_scope);
continue;
}
shared = finder.Result();
if (shared.is_null()) break;
// We found it if it's already compiled.
is_compiled_scope = shared.is_compiled_scope(isolate_);
if (is_compiled_scope.is_compiled()) {

View File

@ -0,0 +1,5 @@
Checks if we keep alive breakpoint information for top-level functions.
Result of setting breakpoint in topLevel.js
[{"scriptId":"3","lineNumber":0,"columnNumber":0}]
Result of setting breakpoint in moduleFunc.js
[{"scriptId":"5","lineNumber":0,"columnNumber":22}]

View File

@ -0,0 +1,52 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
let {session, contextGroup, Protocol} = InspectorTest.start(
'Checks if we keep alive breakpoint information for top-level functions.');
session.setupScriptMap();
var executionContextId;
const callGarbageCollector = `
%CollectGarbage("");
%CollectGarbage("");
%CollectGarbage("");
%CollectGarbage("");
`;
const topLevelFunction = `console.log('This is a top level function')`;
const moduleFunction =
`function testFunc() { console.log('This is a module function') }`;
Protocol.Debugger.enable().then(onDebuggerEnabled);
function onDebuggerEnabled() {
Protocol.Runtime.enable();
Protocol.Runtime.onExecutionContextCreated(onExecutionContextCreated);
}
async function onExecutionContextCreated(messageObject) {
executionContextId = messageObject.params.context.id;
await testSetBreakpoint(executionContextId, topLevelFunction, 'topLevel.js');
await testSetBreakpoint(executionContextId, moduleFunction, 'moduleFunc.js');
InspectorTest.completeTest();
}
async function testSetBreakpoint(executionContextId, func, url) {
const obj = await Protocol.Runtime.compileScript({
expression: func,
sourceURL: url,
persistScript: true,
executionContextId: executionContextId
});
const scriptId = obj.result.scriptId;
await Protocol.Runtime.runScript({scriptId});
await Protocol.Runtime.evaluate({expression: `${callGarbageCollector}`});
const {result: {locations}} =
await Protocol.Debugger.setBreakpointByUrl({lineNumber: 0, url});
InspectorTest.log(`Result of setting breakpoint in ${url}`);
InspectorTest.log(JSON.stringify(locations));
}