LibJS: Don't overwrite cached this value on async/generator reentry

When resuming execution of a suspended function, we must not overwrite
any cached `this` value with something from the ExecutionContext.

This was causing an empty JS::Value to leak into the VM when resuming
an async arrow function, since the "this mode" for such functions is
lexical and thus ExecutionContext will have an empty `this`.

It became a problem due to the bytecode optimization where we allow
ourselves to assume that `this` remains cached after we've executed a
ResolveThisBinding in this (or the first) basic block of the executable.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/138
This commit is contained in:
Andreas Kling 2024-06-20 09:51:06 +02:00 committed by Andreas Kling
parent ad10705615
commit a91bb72dab
Notes: sideshowbarker 2024-07-17 07:25:39 +09:00
2 changed files with 15 additions and 1 deletions

View file

@ -717,7 +717,13 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
reg(Register::accumulator()) = initial_accumulator_value;
reg(Register::return_value()) = {};
reg(Register::this_value()) = running_execution_context.this_value;
// NOTE: We only copy the `this` value from ExecutionContext if it's not already set.
// If we are re-entering an async/generator context, the `this` value
// may have already been cached by a ResolveThisBinding instruction,
// and subsequent instructions expect this value to be set.
if (reg(Register::this_value()).is_empty())
reg(Register::this_value()) = running_execution_context.this_value;
running_execution_context.executable = &executable;

View file

@ -0,0 +1,8 @@
test("this value in async function", () => {
function X() {
this.boog = async () => {
this.f = await null;
};
}
new X().boog();
});