mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 14:05:07 +00:00
LibJS/Bytecode: Create global variables before setting them
This allows them to be accessed before assignment, and also prevents throwing in strict mode as we are trying to set a non-existent variable.
This commit is contained in:
parent
12e3abc9e7
commit
c55a4c7f30
3 changed files with 25 additions and 13 deletions
|
@ -181,9 +181,7 @@ Bytecode::CodeGenerationErrorOr<void> ScopeNode::generate_bytecode(Bytecode::Gen
|
||||||
// ii. If declaredFunctionOrVarNames does not contain F, then
|
// ii. If declaredFunctionOrVarNames does not contain F, then
|
||||||
if (!declared_function_names.contains(function_name) && !declared_var_names.contains(function_name)) {
|
if (!declared_function_names.contains(function_name) && !declared_var_names.contains(function_name)) {
|
||||||
// i. Perform ? env.CreateGlobalVarBinding(F, false).
|
// i. Perform ? env.CreateGlobalVarBinding(F, false).
|
||||||
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Var, false);
|
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Var, false, true);
|
||||||
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
|
|
||||||
generator.emit<Bytecode::Op::SetVariable>(index, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Var);
|
|
||||||
|
|
||||||
// ii. Append F to declaredFunctionOrVarNames.
|
// ii. Append F to declaredFunctionOrVarNames.
|
||||||
declared_function_names.set(function_name);
|
declared_function_names.set(function_name);
|
||||||
|
@ -238,9 +236,12 @@ Bytecode::CodeGenerationErrorOr<void> ScopeNode::generate_bytecode(Bytecode::Gen
|
||||||
}
|
}
|
||||||
|
|
||||||
// 17. For each String vn of declaredVarNames, do
|
// 17. For each String vn of declaredVarNames, do
|
||||||
// a. Perform ? env.CreateGlobalVarBinding(vn, false).
|
for (auto& var_name : declared_var_names) {
|
||||||
for (auto& var_name : declared_var_names)
|
// a. Perform ? env.CreateGlobalVarBinding(vn, false).
|
||||||
generator.register_binding(generator.intern_identifier(var_name), Bytecode::Generator::BindingMode::Var);
|
auto index = generator.intern_identifier(var_name);
|
||||||
|
generator.register_binding(index, Bytecode::Generator::BindingMode::Var);
|
||||||
|
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Var, false, true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Perform the steps of FunctionDeclarationInstantiation.
|
// Perform the steps of FunctionDeclarationInstantiation.
|
||||||
generator.begin_variable_scope(Bytecode::Generator::BindingMode::Var, Bytecode::Generator::SurroundingScopeKind::Function);
|
generator.begin_variable_scope(Bytecode::Generator::BindingMode::Var, Bytecode::Generator::SurroundingScopeKind::Function);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
#include <LibJS/Runtime/Environment.h>
|
#include <LibJS/Runtime/Environment.h>
|
||||||
|
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/Iterator.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
#include <LibJS/Runtime/IteratorOperations.h>
|
#include <LibJS/Runtime/IteratorOperations.h>
|
||||||
|
@ -339,6 +340,8 @@ ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& inte
|
||||||
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
|
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
|
||||||
|
|
||||||
if (m_mode == EnvironmentMode::Lexical) {
|
if (m_mode == EnvironmentMode::Lexical) {
|
||||||
|
VERIFY(!m_is_global);
|
||||||
|
|
||||||
// Note: This is papering over an issue where "FunctionDeclarationInstantiation" creates these bindings for us.
|
// Note: This is papering over an issue where "FunctionDeclarationInstantiation" creates these bindings for us.
|
||||||
// Instead of crashing in there, we'll just raise an exception here.
|
// Instead of crashing in there, we'll just raise an exception here.
|
||||||
if (TRY(vm.lexical_environment()->has_binding(name)))
|
if (TRY(vm.lexical_environment()->has_binding(name)))
|
||||||
|
@ -349,10 +352,16 @@ ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& inte
|
||||||
else
|
else
|
||||||
vm.lexical_environment()->create_mutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
|
vm.lexical_environment()->create_mutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
|
||||||
} else {
|
} else {
|
||||||
if (m_is_immutable)
|
if (!m_is_global) {
|
||||||
vm.variable_environment()->create_immutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
|
if (m_is_immutable)
|
||||||
else
|
vm.variable_environment()->create_immutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
|
||||||
vm.variable_environment()->create_mutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
|
else
|
||||||
|
vm.variable_environment()->create_mutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
|
||||||
|
} else {
|
||||||
|
// NOTE: CreateVariable with m_is_global set to true is expected to only be used in GlobalDeclarationInstantiation currently, which only uses "false" for "can_be_deleted".
|
||||||
|
// The only area that sets "can_be_deleted" to true is EvalDeclarationInstantiation, which is currently fully implemented in C++ and not in Bytecode.
|
||||||
|
verify_cast<GlobalEnvironment>(vm.variable_environment())->create_global_var_binding(name, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -882,7 +891,7 @@ String CreateEnvironment::to_string_impl(Bytecode::Executable const&) const
|
||||||
String CreateVariable::to_string_impl(Bytecode::Executable const& executable) const
|
String CreateVariable::to_string_impl(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable";
|
auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable";
|
||||||
return String::formatted("CreateVariable env:{} immutable:{} {} ({})", mode_string, m_is_immutable, m_identifier, executable.identifier_table->get(m_identifier));
|
return String::formatted("CreateVariable env:{} immutable:{} global:{} {} ({})", mode_string, m_is_immutable, m_is_global, m_identifier, executable.identifier_table->get(m_identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
String EnterObjectEnvironment::to_string_impl(Executable const&) const
|
String EnterObjectEnvironment::to_string_impl(Executable const&) const
|
||||||
|
|
|
@ -317,11 +317,12 @@ public:
|
||||||
|
|
||||||
class CreateVariable final : public Instruction {
|
class CreateVariable final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable)
|
explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable, bool is_global = false)
|
||||||
: Instruction(Type::CreateVariable)
|
: Instruction(Type::CreateVariable)
|
||||||
, m_identifier(identifier)
|
, m_identifier(identifier)
|
||||||
, m_mode(mode)
|
, m_mode(mode)
|
||||||
, m_is_immutable(is_immutable)
|
, m_is_immutable(is_immutable)
|
||||||
|
, m_is_global(is_global)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +333,8 @@ public:
|
||||||
private:
|
private:
|
||||||
IdentifierTableIndex m_identifier;
|
IdentifierTableIndex m_identifier;
|
||||||
EnvironmentMode m_mode;
|
EnvironmentMode m_mode;
|
||||||
bool m_is_immutable { false };
|
bool m_is_immutable : 4 { false };
|
||||||
|
bool m_is_global : 4 { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetVariable final : public Instruction {
|
class SetVariable final : public Instruction {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue