ladybird/Libraries/LibJS/Runtime/Environment.cpp

41 lines
1 KiB
C++
Raw Permalink Normal View History

/*
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Environment.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
Environment::Environment(Environment* outer_environment, IsDeclarative is_declarative)
: m_declarative(is_declarative == IsDeclarative::Yes)
, m_outer_environment(outer_environment)
{
}
void Environment::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_outer_environment);
}
void Environment::set_permanently_screwed_by_eval()
{
if (m_permanently_screwed_by_eval)
return;
m_permanently_screwed_by_eval = true;
LibJS: Limit eval() deoptimization to the containing function scope Previously, when direct eval() was called, we would mark the entire environment chain as "permanently screwed by eval", disabling variable access caching all the way up to the global scope. This was overly conservative. According to the ECMAScript specification, a sloppy direct eval() can only inject var declarations into its containing function's variable environment - it cannot inject variables into parent function scopes. This patch makes two changes: 1. Stop propagating the "screwed by eval" flag at function boundaries. When set_permanently_screwed_by_eval() hits a FunctionEnvironment or GlobalEnvironment, it no longer continues to outer environments. 2. Check each environment during cache lookup traversal. If any environment in the path is marked as screwed, we bail to the slow path. This catches the case where we're inside a function with eval and have a cached coordinate pointing to an outer scope. The second change is necessary because eval can create local variables that shadow outer bindings. When looking up a variable from inside a function that called eval, we can't trust cached coordinates that point to outer scopes, since eval may have created a closer binding. This improves performance for code with nested functions where an inner function uses eval but parent functions perform many variable accesses. The parent functions can now use cached environment coordinates. All 29 new tests verify behavior matches V8.
2026-01-26 20:40:05 +01:00
// Stop propagation at function or global boundaries.
// Eval can only inject vars into its containing function's variable environment,
// not into parent function scopes.
if (is_function_environment() || is_global_environment())
return;
if (outer_environment())
outer_environment()->set_permanently_screwed_by_eval();
}
}