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()