LibJS: Move "strict mode" state to the call stack

Each call frame now knows whether it's executing in strict mode.
It's no longer necessary to access the scope stack to find this mode.
This commit is contained in:
Andreas Kling 2020-10-04 13:54:44 +02:00
parent f41b5a4535
commit a007b3c379
Notes: sideshowbarker 2024-07-19 02:04:33 +09:00
15 changed files with 49 additions and 37 deletions

View file

@ -91,7 +91,7 @@ Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object)
Value Program::execute(Interpreter& interpreter, GlobalObject& global_object) const Value Program::execute(Interpreter& interpreter, GlobalObject& global_object) const
{ {
return interpreter.execute_statement(global_object, *this, {}, ScopeType::Block, m_is_strict_mode); return interpreter.execute_statement(global_object, *this, {}, ScopeType::Block);
} }
Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const
@ -101,7 +101,7 @@ Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const
Value FunctionExpression::execute(Interpreter& interpreter, GlobalObject& global_object) const Value FunctionExpression::execute(Interpreter& interpreter, GlobalObject& global_object) const
{ {
return ScriptFunction::create(global_object, name(), body(), parameters(), function_length(), interpreter.current_environment(), is_strict_mode(), m_is_arrow_function); return ScriptFunction::create(global_object, name(), body(), parameters(), function_length(), interpreter.current_environment(), is_strict_mode() || interpreter.vm().in_strict_mode(), m_is_arrow_function);
} }
Value ExpressionStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const Value ExpressionStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const

View file

@ -72,6 +72,7 @@ Value Interpreter::run(GlobalObject& global_object, const Program& program)
global_call_frame.function_name = "(global execution context)"; global_call_frame.function_name = "(global execution context)";
global_call_frame.environment = heap().allocate<LexicalEnvironment>(global_object, LexicalEnvironment::EnvironmentRecordType::Global); global_call_frame.environment = heap().allocate<LexicalEnvironment>(global_object, LexicalEnvironment::EnvironmentRecordType::Global);
global_call_frame.environment->bind_this_value(global_object, &global_object); global_call_frame.environment->bind_this_value(global_object, &global_object);
global_call_frame.is_strict_mode = program.is_strict_mode();
if (vm().exception()) if (vm().exception())
return {}; return {};
vm().call_stack().append(move(global_call_frame)); vm().call_stack().append(move(global_call_frame));
@ -91,15 +92,15 @@ const GlobalObject& Interpreter::global_object() const
return static_cast<const GlobalObject&>(*m_global_object.cell()); return static_cast<const GlobalObject&>(*m_global_object.cell());
} }
void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type, GlobalObject& global_object, bool is_strict) void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type, GlobalObject& global_object)
{ {
for (auto& declaration : scope_node.functions()) { for (auto& declaration : scope_node.functions()) {
auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_environment(), is_strict); auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_environment(), declaration.is_strict_mode());
vm().set_variable(declaration.name(), function, global_object); vm().set_variable(declaration.name(), function, global_object);
} }
if (scope_type == ScopeType::Function) { if (scope_type == ScopeType::Function) {
push_scope({ scope_type, scope_node, false, is_strict }); push_scope({ scope_type, scope_node, false });
return; return;
} }
@ -130,7 +131,7 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector argume
pushed_lexical_environment = true; pushed_lexical_environment = true;
} }
push_scope({ scope_type, scope_node, pushed_lexical_environment, is_strict }); push_scope({ scope_type, scope_node, pushed_lexical_environment });
} }
void Interpreter::exit_scope(const ScopeNode& scope_node) void Interpreter::exit_scope(const ScopeNode& scope_node)
@ -150,18 +151,16 @@ void Interpreter::exit_scope(const ScopeNode& scope_node)
void Interpreter::push_scope(ScopeFrame frame) void Interpreter::push_scope(ScopeFrame frame)
{ {
if (in_strict_mode())
frame.is_strict_mode = true;
m_scope_stack.append(move(frame)); m_scope_stack.append(move(frame));
} }
Value Interpreter::execute_statement(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type, bool is_strict) Value Interpreter::execute_statement(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type)
{ {
if (!statement.is_scope_node()) if (!statement.is_scope_node())
return statement.execute(*this, global_object); return statement.execute(*this, global_object);
auto& block = static_cast<const ScopeNode&>(statement); auto& block = static_cast<const ScopeNode&>(statement);
enter_scope(block, move(arguments), scope_type, global_object, is_strict); enter_scope(block, move(arguments), scope_type, global_object);
if (block.children().is_empty()) if (block.children().is_empty())
vm().set_last_value({}, js_undefined()); vm().set_last_value({}, js_undefined());

View file

@ -86,23 +86,16 @@ public:
Heap& heap() { return vm().heap(); } Heap& heap() { return vm().heap(); }
Exception* exception() { return vm().exception(); } Exception* exception() { return vm().exception(); }
bool in_strict_mode() const
{
if (m_scope_stack.is_empty())
return false;
return m_scope_stack.last().is_strict_mode;
}
size_t argument_count() const { return vm().argument_count(); } size_t argument_count() const { return vm().argument_count(); }
Value argument(size_t index) const { return vm().argument(index); } Value argument(size_t index) const { return vm().argument(index); }
Value this_value(Object& global_object) const { return vm().this_value(global_object); } Value this_value(Object& global_object) const { return vm().this_value(global_object); }
LexicalEnvironment* current_environment() { return vm().current_environment(); } LexicalEnvironment* current_environment() { return vm().current_environment(); }
const CallFrame& call_frame() { return vm().call_frame(); } const CallFrame& call_frame() { return vm().call_frame(); }
void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&, bool is_strict = false); void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&);
void exit_scope(const ScopeNode&); void exit_scope(const ScopeNode&);
Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block, bool is_strict = false); Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block);
private: private:
explicit Interpreter(VM&); explicit Interpreter(VM&);

View file

@ -56,6 +56,8 @@ public:
return *m_target_function; return *m_target_function;
} }
virtual bool is_strict_mode() const override { return m_target_function->is_strict_mode(); }
private: private:
virtual bool is_bound_function() const override { return true; } virtual bool is_bound_function() const override { return true; }

View file

@ -57,7 +57,7 @@ BoundFunction* Function::bind(Value bound_this_value, Vector<Value> arguments)
switch (bound_this_value.type()) { switch (bound_this_value.type()) {
case Value::Type::Undefined: case Value::Type::Undefined:
case Value::Type::Null: case Value::Type::Null:
if (interpreter().in_strict_mode()) if (vm().in_strict_mode())
return bound_this_value; return bound_this_value;
return &global_object(); return &global_object();
default: default:

View file

@ -64,6 +64,8 @@ public:
ConstructorKind constructor_kind() const { return m_constructor_kind; }; ConstructorKind constructor_kind() const { return m_constructor_kind; };
void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; } void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; }
virtual bool is_strict_mode() const { return false; }
protected: protected:
explicit Function(Object& prototype); explicit Function(Object& prototype);
Function(Object& prototype, Value bound_this, Vector<Value> bound_arguments); Function(Object& prototype, Value bound_this, Vector<Value> bound_arguments);

View file

@ -73,4 +73,9 @@ LexicalEnvironment* NativeFunction::create_environment()
return heap().allocate<LexicalEnvironment>(global_object(), LexicalEnvironment::EnvironmentRecordType::Function); return heap().allocate<LexicalEnvironment>(global_object(), LexicalEnvironment::EnvironmentRecordType::Function);
} }
bool NativeFunction::is_strict_mode() const
{
return vm().in_strict_mode();
}
} }

View file

@ -47,6 +47,8 @@ public:
virtual const FlyString& name() const override { return m_name; }; virtual const FlyString& name() const override { return m_name; };
virtual bool has_constructor() const { return false; } virtual bool has_constructor() const { return false; }
virtual bool is_strict_mode() const override;
protected: protected:
NativeFunction(const FlyString& name, Object& prototype); NativeFunction(const FlyString& name, Object& prototype);
explicit NativeFunction(Object& prototype); explicit NativeFunction(Object& prototype);

View file

@ -26,7 +26,6 @@
#include <AK/String.h> #include <AK/String.h>
#include <LibJS/Heap/Heap.h> #include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Accessor.h> #include <LibJS/Runtime/Accessor.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Error.h>
@ -464,7 +463,7 @@ bool Object::put_own_property(Object& this_object, const StringOrSymbol& propert
#ifdef OBJECT_DEBUG #ifdef OBJECT_DEBUG
dbg() << "Disallow define_property of non-extensible object"; dbg() << "Disallow define_property of non-extensible object";
#endif #endif
if (throw_exceptions && interpreter().in_strict_mode()) if (throw_exceptions && vm().in_strict_mode())
vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_name.to_display_string().characters()); vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_name.to_display_string().characters());
return false; return false;
} }
@ -546,7 +545,7 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index,
#ifdef OBJECT_DEBUG #ifdef OBJECT_DEBUG
dbg() << "Disallow define_property of non-extensible object"; dbg() << "Disallow define_property of non-extensible object";
#endif #endif
if (throw_exceptions && interpreter().in_strict_mode()) if (throw_exceptions && vm().in_strict_mode())
vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_index); vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_index);
return false; return false;
} }
@ -870,7 +869,7 @@ Value Object::invoke(const StringOrSymbol& property_name, Optional<MarkedValueLi
Value Object::call_native_property_getter(Object* this_object, Value property) const Value Object::call_native_property_getter(Object* this_object, Value property) const
{ {
ASSERT(property.is_native_property()); ASSERT(property.is_native_property());
auto& call_frame = vm().push_call_frame(); auto& call_frame = vm().push_call_frame(vm().in_strict_mode());
call_frame.this_value = this_object; call_frame.this_value = this_object;
auto result = property.as_native_property().get(vm(), global_object()); auto result = property.as_native_property().get(vm(), global_object());
vm().pop_call_frame(); vm().pop_call_frame();
@ -880,7 +879,7 @@ Value Object::call_native_property_getter(Object* this_object, Value property) c
void Object::call_native_property_setter(Object* this_object, Value property, Value value) const void Object::call_native_property_setter(Object* this_object, Value property, Value value) const
{ {
ASSERT(property.is_native_property()); ASSERT(property.is_native_property());
auto& call_frame = vm().push_call_frame(); auto& call_frame = vm().push_call_frame(vm().in_strict_mode());
call_frame.this_value = this_object; call_frame.this_value = this_object;
property.as_native_property().set(vm(), global_object(), value); property.as_native_property().set(vm(), global_object(), value);
vm().pop_call_frame(); vm().pop_call_frame();

View file

@ -25,7 +25,6 @@
*/ */
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>
@ -50,7 +49,7 @@ void Reference::put(GlobalObject& global_object, Value value)
return; return;
} }
if (!base().is_object() && vm.interpreter().in_strict_mode()) { if (!base().is_object() && vm.in_strict_mode()) {
vm.throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_string().characters()); vm.throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_string().characters());
return; return;
} }

View file

@ -141,7 +141,8 @@ Value ScriptFunction::call()
arguments.append({ parameter.name, value }); arguments.append({ parameter.name, value });
vm().current_environment()->set(global_object(), parameter.name, { value, DeclarationKind::Var }); vm().current_environment()->set(global_object(), parameter.name, { value, DeclarationKind::Var });
} }
return interpreter->execute_statement(global_object(), m_body, arguments, ScopeType::Function, m_is_strict);
return interpreter->execute_statement(global_object(), m_body, arguments, ScopeType::Function);
} }
Value ScriptFunction::construct(Function&) Value ScriptFunction::construct(Function&)

View file

@ -50,6 +50,9 @@ public:
virtual const FlyString& name() const override { return m_name; }; virtual const FlyString& name() const override { return m_name; };
void set_name(const FlyString& name) { m_name = name; }; void set_name(const FlyString& name) { m_name = name; };
protected:
virtual bool is_strict_mode() const final { return m_is_strict; }
private: private:
virtual bool is_script_function() const override { return true; } virtual bool is_script_function() const override { return true; }
virtual LexicalEnvironment* create_environment() override; virtual LexicalEnvironment* create_environment() override;
@ -62,9 +65,9 @@ private:
NonnullRefPtr<Statement> m_body; NonnullRefPtr<Statement> m_body;
const Vector<FunctionNode::Parameter> m_parameters; const Vector<FunctionNode::Parameter> m_parameters;
LexicalEnvironment* m_parent_environment { nullptr }; LexicalEnvironment* m_parent_environment { nullptr };
i32 m_function_length; i32 m_function_length { 0 };
bool m_is_strict; bool m_is_strict { false };
bool m_is_arrow_function; bool m_is_arrow_function { false };
}; };
} }

View file

@ -187,7 +187,7 @@ Reference VM::get_reference(const FlyString& name)
Value VM::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object) Value VM::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object)
{ {
auto& call_frame = push_call_frame(); auto& call_frame = push_call_frame(function.is_strict_mode());
ArmedScopeGuard call_frame_popper = [&] { ArmedScopeGuard call_frame_popper = [&] {
pop_call_frame(); pop_call_frame();
@ -304,7 +304,7 @@ Value VM::call_internal(Function& function, Value this_value, Optional<MarkedVal
{ {
ASSERT(!exception()); ASSERT(!exception());
auto& call_frame = push_call_frame(); auto& call_frame = push_call_frame(function.is_strict_mode());
call_frame.function_name = function.name(); call_frame.function_name = function.name();
call_frame.this_value = function.bound_this().value_or(this_value); call_frame.this_value = function.bound_this().value_or(this_value);
call_frame.arguments = function.bound_arguments(); call_frame.arguments = function.bound_arguments();
@ -320,4 +320,11 @@ Value VM::call_internal(Function& function, Value this_value, Optional<MarkedVal
return result; return result;
} }
bool VM::in_strict_mode() const
{
if (call_stack().is_empty())
return false;
return call_frame().is_strict_mode;
}
} }

View file

@ -50,7 +50,6 @@ struct ScopeFrame {
ScopeType type; ScopeType type;
NonnullRefPtr<ScopeNode> scope_node; NonnullRefPtr<ScopeNode> scope_node;
bool pushed_environment { false }; bool pushed_environment { false };
bool is_strict_mode { false };
}; };
struct CallFrame { struct CallFrame {
@ -58,6 +57,7 @@ struct CallFrame {
Value this_value; Value this_value;
Vector<Value> arguments; Vector<Value> arguments;
LexicalEnvironment* environment { nullptr }; LexicalEnvironment* environment { nullptr };
bool is_strict_mode { false };
}; };
struct Argument { struct Argument {
@ -108,9 +108,9 @@ public:
PrimitiveString& empty_string() { return *m_empty_string; } PrimitiveString& empty_string() { return *m_empty_string; }
CallFrame& push_call_frame() CallFrame& push_call_frame(bool strict_mode = false)
{ {
m_call_stack.append({ {}, js_undefined(), {}, nullptr }); m_call_stack.append({ {}, js_undefined(), {}, nullptr, strict_mode });
return m_call_stack.last(); return m_call_stack.last();
} }
void pop_call_frame() { m_call_stack.take_last(); } void pop_call_frame() { m_call_stack.take_last(); }

View file

@ -160,7 +160,7 @@ void TestRunnerGlobalObject::initialize()
JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::is_strict_mode) JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::is_strict_mode)
{ {
return JS::Value(vm.interpreter().in_strict_mode()); return JS::Value(vm.in_strict_mode());
} }
static void cleanup_and_exit() static void cleanup_and_exit()