From a007b3c379bd28795eebd93877eced138b51f739 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 4 Oct 2020 13:54:44 +0200 Subject: [PATCH] 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. --- Libraries/LibJS/AST.cpp | 4 ++-- Libraries/LibJS/Interpreter.cpp | 15 +++++++-------- Libraries/LibJS/Interpreter.h | 11 ++--------- Libraries/LibJS/Runtime/BoundFunction.h | 2 ++ Libraries/LibJS/Runtime/Function.cpp | 2 +- Libraries/LibJS/Runtime/Function.h | 2 ++ Libraries/LibJS/Runtime/NativeFunction.cpp | 5 +++++ Libraries/LibJS/Runtime/NativeFunction.h | 2 ++ Libraries/LibJS/Runtime/Object.cpp | 9 ++++----- Libraries/LibJS/Runtime/Reference.cpp | 3 +-- Libraries/LibJS/Runtime/ScriptFunction.cpp | 3 ++- Libraries/LibJS/Runtime/ScriptFunction.h | 9 ++++++--- Libraries/LibJS/Runtime/VM.cpp | 11 +++++++++-- Libraries/LibJS/Runtime/VM.h | 6 +++--- Userland/test-js.cpp | 2 +- 15 files changed, 49 insertions(+), 37 deletions(-) diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index ccdb818d1b6..b355d2a562f 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -91,7 +91,7 @@ Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object) 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 @@ -101,7 +101,7 @@ Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) 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 diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index a1351e7c649..51e5cf0a3cd 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -72,6 +72,7 @@ Value Interpreter::run(GlobalObject& global_object, const Program& program) global_call_frame.function_name = "(global execution context)"; global_call_frame.environment = heap().allocate(global_object, LexicalEnvironment::EnvironmentRecordType::Global); global_call_frame.environment->bind_this_value(global_object, &global_object); + global_call_frame.is_strict_mode = program.is_strict_mode(); if (vm().exception()) return {}; vm().call_stack().append(move(global_call_frame)); @@ -91,15 +92,15 @@ const GlobalObject& Interpreter::global_object() const return static_cast(*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()) { - 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); } if (scope_type == ScopeType::Function) { - push_scope({ scope_type, scope_node, false, is_strict }); + push_scope({ scope_type, scope_node, false }); return; } @@ -130,7 +131,7 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector argume 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) @@ -150,18 +151,16 @@ void Interpreter::exit_scope(const ScopeNode& scope_node) void Interpreter::push_scope(ScopeFrame frame) { - if (in_strict_mode()) - frame.is_strict_mode = true; 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()) return statement.execute(*this, global_object); auto& block = static_cast(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()) vm().set_last_value({}, js_undefined()); diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 40c4cc42c44..cd67485f7a1 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -86,23 +86,16 @@ public: Heap& heap() { return vm().heap(); } 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(); } Value argument(size_t index) const { return vm().argument(index); } Value this_value(Object& global_object) const { return vm().this_value(global_object); } LexicalEnvironment* current_environment() { return vm().current_environment(); } 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&); - 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: explicit Interpreter(VM&); diff --git a/Libraries/LibJS/Runtime/BoundFunction.h b/Libraries/LibJS/Runtime/BoundFunction.h index f86f58e93e6..62e9d419186 100644 --- a/Libraries/LibJS/Runtime/BoundFunction.h +++ b/Libraries/LibJS/Runtime/BoundFunction.h @@ -56,6 +56,8 @@ public: return *m_target_function; } + virtual bool is_strict_mode() const override { return m_target_function->is_strict_mode(); } + private: virtual bool is_bound_function() const override { return true; } diff --git a/Libraries/LibJS/Runtime/Function.cpp b/Libraries/LibJS/Runtime/Function.cpp index f7e5e66c266..1881a079b56 100644 --- a/Libraries/LibJS/Runtime/Function.cpp +++ b/Libraries/LibJS/Runtime/Function.cpp @@ -57,7 +57,7 @@ BoundFunction* Function::bind(Value bound_this_value, Vector arguments) switch (bound_this_value.type()) { case Value::Type::Undefined: case Value::Type::Null: - if (interpreter().in_strict_mode()) + if (vm().in_strict_mode()) return bound_this_value; return &global_object(); default: diff --git a/Libraries/LibJS/Runtime/Function.h b/Libraries/LibJS/Runtime/Function.h index 6c5832fc3fd..0833acf2dbc 100644 --- a/Libraries/LibJS/Runtime/Function.h +++ b/Libraries/LibJS/Runtime/Function.h @@ -64,6 +64,8 @@ public: ConstructorKind constructor_kind() const { return m_constructor_kind; }; void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; } + virtual bool is_strict_mode() const { return false; } + protected: explicit Function(Object& prototype); Function(Object& prototype, Value bound_this, Vector bound_arguments); diff --git a/Libraries/LibJS/Runtime/NativeFunction.cpp b/Libraries/LibJS/Runtime/NativeFunction.cpp index 946ce4e732b..8453724414c 100644 --- a/Libraries/LibJS/Runtime/NativeFunction.cpp +++ b/Libraries/LibJS/Runtime/NativeFunction.cpp @@ -73,4 +73,9 @@ LexicalEnvironment* NativeFunction::create_environment() return heap().allocate(global_object(), LexicalEnvironment::EnvironmentRecordType::Function); } +bool NativeFunction::is_strict_mode() const +{ + return vm().in_strict_mode(); +} + } diff --git a/Libraries/LibJS/Runtime/NativeFunction.h b/Libraries/LibJS/Runtime/NativeFunction.h index 6d0f3bca848..9f9f966684e 100644 --- a/Libraries/LibJS/Runtime/NativeFunction.h +++ b/Libraries/LibJS/Runtime/NativeFunction.h @@ -47,6 +47,8 @@ public: virtual const FlyString& name() const override { return m_name; }; virtual bool has_constructor() const { return false; } + virtual bool is_strict_mode() const override; + protected: NativeFunction(const FlyString& name, Object& prototype); explicit NativeFunction(Object& prototype); diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index 19ffdcea208..deaaec65549 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -464,7 +463,7 @@ bool Object::put_own_property(Object& this_object, const StringOrSymbol& propert #ifdef OBJECT_DEBUG dbg() << "Disallow define_property of non-extensible object"; #endif - if (throw_exceptions && interpreter().in_strict_mode()) + if (throw_exceptions && vm().in_strict_mode()) vm().throw_exception(global_object(), ErrorType::NonExtensibleDefine, property_name.to_display_string().characters()); return false; } @@ -546,7 +545,7 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index, #ifdef OBJECT_DEBUG dbg() << "Disallow define_property of non-extensible object"; #endif - if (throw_exceptions && interpreter().in_strict_mode()) + if (throw_exceptions && vm().in_strict_mode()) vm().throw_exception(global_object(), ErrorType::NonExtensibleDefine, property_index); return false; } @@ -870,7 +869,7 @@ Value Object::invoke(const StringOrSymbol& property_name, Optional -#include #include #include #include @@ -50,7 +49,7 @@ void Reference::put(GlobalObject& global_object, Value value) return; } - if (!base().is_object() && vm.interpreter().in_strict_mode()) { + if (!base().is_object() && vm.in_strict_mode()) { vm.throw_exception(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_string().characters()); return; } diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp index 76a001c131f..2e012d5903c 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -141,7 +141,8 @@ Value ScriptFunction::call() arguments.append({ parameter.name, value }); 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&) diff --git a/Libraries/LibJS/Runtime/ScriptFunction.h b/Libraries/LibJS/Runtime/ScriptFunction.h index 335ca441d5b..0bb2e0a2795 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.h +++ b/Libraries/LibJS/Runtime/ScriptFunction.h @@ -50,6 +50,9 @@ public: virtual const FlyString& name() const override { return m_name; }; void set_name(const FlyString& name) { m_name = name; }; +protected: + virtual bool is_strict_mode() const final { return m_is_strict; } + private: virtual bool is_script_function() const override { return true; } virtual LexicalEnvironment* create_environment() override; @@ -62,9 +65,9 @@ private: NonnullRefPtr m_body; const Vector m_parameters; LexicalEnvironment* m_parent_environment { nullptr }; - i32 m_function_length; - bool m_is_strict; - bool m_is_arrow_function; + i32 m_function_length { 0 }; + bool m_is_strict { false }; + bool m_is_arrow_function { false }; }; } diff --git a/Libraries/LibJS/Runtime/VM.cpp b/Libraries/LibJS/Runtime/VM.cpp index 41919e2fbd2..40c48102fdc 100644 --- a/Libraries/LibJS/Runtime/VM.cpp +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -187,7 +187,7 @@ Reference VM::get_reference(const FlyString& name) Value VM::construct(Function& function, Function& new_target, Optional arguments, GlobalObject& global_object) { - auto& call_frame = push_call_frame(); + auto& call_frame = push_call_frame(function.is_strict_mode()); ArmedScopeGuard call_frame_popper = [&] { pop_call_frame(); @@ -304,7 +304,7 @@ Value VM::call_internal(Function& function, Value this_value, Optional scope_node; bool pushed_environment { false }; - bool is_strict_mode { false }; }; struct CallFrame { @@ -58,6 +57,7 @@ struct CallFrame { Value this_value; Vector arguments; LexicalEnvironment* environment { nullptr }; + bool is_strict_mode { false }; }; struct Argument { @@ -108,9 +108,9 @@ public: 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(); } void pop_call_frame() { m_call_stack.take_last(); } diff --git a/Userland/test-js.cpp b/Userland/test-js.cpp index cd704f8a7fd..4bf7141e7d6 100644 --- a/Userland/test-js.cpp +++ b/Userland/test-js.cpp @@ -160,7 +160,7 @@ void TestRunnerGlobalObject::initialize() 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()