[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:
parent
04441c4407
commit
312a3fbec0
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()) {
|
||||
|
@ -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}]
|
52
test/inspector/debugger/set-breakpoint-after-gc.js
Normal file
52
test/inspector/debugger/set-breakpoint-after-gc.js
Normal 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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user