diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index e8880cd4020..4b4e646af49 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -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() || interpreter.vm().in_strict_mode(), m_is_arrow_function); + return ScriptFunction::create(global_object, name(), body(), parameters(), function_length(), interpreter.current_scope(), is_strict_mode() || interpreter.vm().in_strict_mode(), m_is_arrow_function); } Value ExpressionStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const @@ -128,7 +128,7 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete if (m_callee->is_member_expression()) { auto& member_expression = static_cast(*m_callee); bool is_super_property_lookup = member_expression.object().is_super_expression(); - auto lookup_target = is_super_property_lookup ? vm.current_environment()->get_super_base() : member_expression.object().execute(interpreter, global_object); + auto lookup_target = is_super_property_lookup ? interpreter.current_environment()->get_super_base() : member_expression.object().execute(interpreter, global_object); if (vm.exception()) return {}; if (is_super_property_lookup && lookup_target.is_nullish()) { @@ -205,7 +205,7 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj if (result.is_object()) new_object = &result.as_object(); } else if (m_callee->is_super_expression()) { - auto* super_constructor = vm.current_environment()->current_function()->prototype(); + auto* super_constructor = interpreter.current_environment()->current_function()->prototype(); // FIXME: Functions should track their constructor kind. if (!super_constructor || !super_constructor->is_function()) { vm.throw_exception(global_object, ErrorType::NotAConstructor, "Super constructor"); @@ -215,7 +215,7 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj if (vm.exception()) return {}; - vm.current_environment()->bind_this_value(global_object, result); + interpreter.current_environment()->bind_this_value(global_object, result); } else { result = vm.call(function, this_value, move(arguments)); } @@ -801,7 +801,7 @@ Value ClassDeclaration::execute(Interpreter& interpreter, GlobalObject& global_o if (interpreter.exception()) return {}; - interpreter.current_environment()->set(global_object, m_class_expression->name(), { class_constructor, DeclarationKind::Let }); + interpreter.current_scope()->put_to_scope(m_class_expression->name(), { class_constructor, DeclarationKind::Let }); return js_undefined(); } diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt index 03323a6b70b..39efe3281d1 100644 --- a/Libraries/LibJS/CMakeLists.txt +++ b/Libraries/LibJS/CMakeLists.txt @@ -61,6 +61,7 @@ set(SOURCES Runtime/RegExpConstructor.cpp Runtime/RegExpObject.cpp Runtime/RegExpPrototype.cpp + Runtime/ScopeObject.cpp Runtime/ScriptFunction.cpp Runtime/Shape.cpp Runtime/StringConstructor.cpp diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 3ad96d44592..57cf1e33dce 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -124,6 +124,7 @@ class NativeProperty; class PrimitiveString; class Reference; class ScopeNode; +class ScopeObject; class Shape; class Statement; class Symbol; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index b6c903643ad..e96ff48c09c 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -72,8 +72,7 @@ Value Interpreter::run(GlobalObject& global_object, const Program& program) global_call_frame.this_value = &global_object; static FlyString global_execution_context_name = "(global execution context)"; global_call_frame.function_name = global_execution_context_name; - 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.scope = &global_object; ASSERT(!vm.exception()); global_call_frame.is_strict_mode = program.is_strict_mode(); vm.push_call_frame(global_call_frame, global_object); @@ -96,7 +95,7 @@ const GlobalObject& Interpreter::global_object() const 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(), declaration.is_strict_mode()); + auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_scope(), declaration.is_strict_mode()); vm().set_variable(declaration.name(), function, global_object); } @@ -127,8 +126,8 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector argume bool pushed_lexical_environment = false; if (!scope_variables_with_declaration_kind.is_empty()) { - auto* block_lexical_environment = heap().allocate(global_object, move(scope_variables_with_declaration_kind), current_environment()); - vm().call_frame().environment = block_lexical_environment; + auto* block_lexical_environment = heap().allocate(global_object, move(scope_variables_with_declaration_kind), current_scope()); + vm().call_frame().scope = block_lexical_environment; pushed_lexical_environment = true; } @@ -140,7 +139,7 @@ void Interpreter::exit_scope(const ScopeNode& scope_node) while (!m_scope_stack.is_empty()) { auto popped_scope = m_scope_stack.take_last(); if (popped_scope.pushed_environment) - vm().call_frame().environment = vm().call_frame().environment->parent(); + vm().call_frame().scope = vm().call_frame().scope->parent(); if (popped_scope.scope_node.ptr() == &scope_node) break; } @@ -185,4 +184,10 @@ Value Interpreter::execute_statement(GlobalObject& global_object, const Statemen return did_return ? vm().last_value() : js_undefined(); } +LexicalEnvironment* Interpreter::current_environment() +{ + ASSERT(vm().call_frame().scope->is_lexical_environment()); + return static_cast(vm().call_frame().scope); +} + } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index c76b6ef1107..e9a89d7993a 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -71,7 +71,8 @@ public: Heap& heap() { return vm().heap(); } Exception* exception() { return vm().exception(); } - LexicalEnvironment* current_environment() { return vm().current_environment(); } + ScopeObject* current_scope() { return vm().current_scope(); } + LexicalEnvironment* current_environment(); void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&); void exit_scope(const ScopeNode&); diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 84e85de8c12..d878996b44e 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -68,7 +68,7 @@ namespace JS { GlobalObject::GlobalObject() - : Object(GlobalObjectTag::Tag) + : ScopeObject(GlobalObjectTag::Tag) , m_console(make(*this)) { } @@ -149,7 +149,7 @@ GlobalObject::~GlobalObject() void GlobalObject::visit_edges(Visitor& visitor) { - Object::visit_edges(visitor); + Base::visit_edges(visitor); visitor.visit(m_empty_object_shape); visitor.visit(m_new_object_shape); @@ -205,4 +205,27 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_float) return js_nan(); } +Optional GlobalObject::get_from_scope(const FlyString& name) const +{ + auto value = get(name); + if (value.is_empty()) + return {}; + return Variable { value, DeclarationKind::Var }; +} + +void GlobalObject::put_to_scope(const FlyString& name, Variable variable) +{ + put(name, variable.value); +} + +bool GlobalObject::has_this_binding() const +{ + return true; +} + +Value GlobalObject::get_this_binding(GlobalObject&) const +{ + return Value(this); +} + } diff --git a/Libraries/LibJS/Runtime/GlobalObject.h b/Libraries/LibJS/Runtime/GlobalObject.h index 1bf49be795a..b0d8214478f 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.h +++ b/Libraries/LibJS/Runtime/GlobalObject.h @@ -27,13 +27,13 @@ #pragma once #include -#include +#include #include namespace JS { -class GlobalObject : public Object { - JS_OBJECT(GlobalObject, Object); +class GlobalObject : public ScopeObject { + JS_OBJECT(GlobalObject, ScopeObject); public: explicit GlobalObject(); @@ -41,6 +41,11 @@ public: virtual ~GlobalObject() override; + virtual Optional get_from_scope(const FlyString&) const override; + virtual void put_to_scope(const FlyString&, Variable) override; + virtual bool has_this_binding() const override; + virtual Value get_this_binding(GlobalObject&) const override; + Console& console() { return *m_console; } Shape* empty_object_shape() { return m_empty_object_shape; } @@ -66,6 +71,8 @@ protected: void add_constructor(const FlyString& property_name, ConstructorType*&, Object& prototype); private: + virtual bool is_global_object() const final { return true; } + JS_DECLARE_NATIVE_FUNCTION(gc); JS_DECLARE_NATIVE_FUNCTION(is_nan); JS_DECLARE_NATIVE_FUNCTION(is_finite); diff --git a/Libraries/LibJS/Runtime/LexicalEnvironment.cpp b/Libraries/LibJS/Runtime/LexicalEnvironment.cpp index 5a8157af2c6..2275b6402b3 100644 --- a/Libraries/LibJS/Runtime/LexicalEnvironment.cpp +++ b/Libraries/LibJS/Runtime/LexicalEnvironment.cpp @@ -34,23 +34,25 @@ namespace JS { LexicalEnvironment::LexicalEnvironment() + : ScopeObject(nullptr) { } LexicalEnvironment::LexicalEnvironment(EnvironmentRecordType environment_record_type) - : m_environment_record_type(environment_record_type) + : ScopeObject(nullptr) + , m_environment_record_type(environment_record_type) { } -LexicalEnvironment::LexicalEnvironment(HashMap variables, LexicalEnvironment* parent) - : m_parent(parent) +LexicalEnvironment::LexicalEnvironment(HashMap variables, ScopeObject* parent_scope) + : ScopeObject(parent_scope) , m_variables(move(variables)) { } -LexicalEnvironment::LexicalEnvironment(HashMap variables, LexicalEnvironment* parent, EnvironmentRecordType environment_record_type) - : m_environment_record_type(environment_record_type) - , m_parent(parent) +LexicalEnvironment::LexicalEnvironment(HashMap variables, ScopeObject* parent_scope, EnvironmentRecordType environment_record_type) + : ScopeObject(parent_scope) + , m_environment_record_type(environment_record_type) , m_variables(move(variables)) { } @@ -62,7 +64,6 @@ LexicalEnvironment::~LexicalEnvironment() void LexicalEnvironment::visit_edges(Visitor& visitor) { Cell::visit_edges(visitor); - visitor.visit(m_parent); visitor.visit(m_this_value); visitor.visit(m_home_object); visitor.visit(m_new_target); @@ -71,18 +72,14 @@ void LexicalEnvironment::visit_edges(Visitor& visitor) visitor.visit(it.value.value); } -Optional LexicalEnvironment::get(const FlyString& name) const +Optional LexicalEnvironment::get_from_scope(const FlyString& name) const { - ASSERT(type() != EnvironmentRecordType::Global); return m_variables.get(name); } -void LexicalEnvironment::set(GlobalObject& global_object, const FlyString& name, Variable variable) +void LexicalEnvironment::put_to_scope(const FlyString& name, Variable variable) { - if (type() == EnvironmentRecordType::Global) - global_object.put(name, variable.value); - else - m_variables.set(name, variable); + m_variables.set(name, variable); } bool LexicalEnvironment::has_super_binding() const @@ -108,7 +105,6 @@ bool LexicalEnvironment::has_this_binding() const case EnvironmentRecordType::Function: return this_binding_status() != ThisBindingStatus::Lexical; case EnvironmentRecordType::Module: - case EnvironmentRecordType::Global: return true; } ASSERT_NOT_REACHED(); diff --git a/Libraries/LibJS/Runtime/LexicalEnvironment.h b/Libraries/LibJS/Runtime/LexicalEnvironment.h index 28a5f2537ad..7dade90a8f6 100644 --- a/Libraries/LibJS/Runtime/LexicalEnvironment.h +++ b/Libraries/LibJS/Runtime/LexicalEnvironment.h @@ -28,17 +28,14 @@ #include #include -#include +#include #include namespace JS { -struct Variable { - Value value; - DeclarationKind declaration_kind; -}; +class LexicalEnvironment final : public ScopeObject { + JS_OBJECT(LexicalEnvironment, ScopeObject); -class LexicalEnvironment final : public Cell { public: enum class ThisBindingStatus { Lexical, @@ -49,21 +46,21 @@ public: enum class EnvironmentRecordType { Declarative, Function, - Global, Object, Module, }; LexicalEnvironment(); LexicalEnvironment(EnvironmentRecordType); - LexicalEnvironment(HashMap variables, LexicalEnvironment* parent); - LexicalEnvironment(HashMap variables, LexicalEnvironment* parent, EnvironmentRecordType); + LexicalEnvironment(HashMap variables, ScopeObject* parent_scope); + LexicalEnvironment(HashMap variables, ScopeObject* parent_scope, EnvironmentRecordType); virtual ~LexicalEnvironment() override; - LexicalEnvironment* parent() const { return m_parent; } - - Optional get(const FlyString&) const; - void set(GlobalObject&, const FlyString&, Variable); + // ^ScopeObject + virtual Optional get_from_scope(const FlyString&) const override; + virtual void put_to_scope(const FlyString&, Variable) override; + virtual bool has_this_binding() const override; + virtual Value get_this_binding(GlobalObject&) const override; void clear(); @@ -73,9 +70,7 @@ public: bool has_super_binding() const; Value get_super_base(); - bool has_this_binding() const; ThisBindingStatus this_binding_status() const { return m_this_binding_status; } - Value get_this_binding(GlobalObject&) const; void bind_this_value(GlobalObject&, Value this_value); // Not a standard operation. @@ -90,12 +85,11 @@ public: EnvironmentRecordType type() const { return m_environment_record_type; } private: - virtual const char* class_name() const override { return "LexicalEnvironment"; } + virtual bool is_lexical_environment() const final { return true; } virtual void visit_edges(Visitor&) override; EnvironmentRecordType m_environment_record_type : 8 { EnvironmentRecordType::Declarative }; ThisBindingStatus m_this_binding_status : 8 { ThisBindingStatus::Uninitialized }; - LexicalEnvironment* m_parent { nullptr }; HashMap m_variables; Value m_home_object; Value m_this_value; diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index a462c43b449..f871d141df2 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -127,6 +127,8 @@ public: virtual bool is_bigint_object() const { return false; } virtual bool is_string_iterator_object() const { return false; } virtual bool is_array_iterator_object() const { return false; } + virtual bool is_lexical_environment() const { return false; } + virtual bool is_global_object() const { return false; } virtual const char* class_name() const override { return "Object"; } virtual void visit_edges(Cell::Visitor&) override; diff --git a/Libraries/LibJS/Runtime/ScopeObject.cpp b/Libraries/LibJS/Runtime/ScopeObject.cpp new file mode 100644 index 00000000000..5a67011a550 --- /dev/null +++ b/Libraries/LibJS/Runtime/ScopeObject.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace JS { + +ScopeObject::ScopeObject(ScopeObject* parent) + : Object(vm().scope_object_shape()) + , m_parent(parent) +{ +} + +ScopeObject::ScopeObject(GlobalObjectTag tag) + : Object(tag) +{ +} + +void ScopeObject::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_parent); +} + +} diff --git a/Libraries/LibJS/Runtime/ScopeObject.h b/Libraries/LibJS/Runtime/ScopeObject.h new file mode 100644 index 00000000000..ac29c115030 --- /dev/null +++ b/Libraries/LibJS/Runtime/ScopeObject.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace JS { + +struct Variable { + Value value; + DeclarationKind declaration_kind; +}; + +class ScopeObject : public Object { + JS_OBJECT(ScopeObject, Object); + +public: + virtual Optional get_from_scope(const FlyString&) const = 0; + virtual void put_to_scope(const FlyString&, Variable) = 0; + virtual bool has_this_binding() const = 0; + virtual Value get_this_binding(GlobalObject&) const = 0; + + ScopeObject* parent() { return m_parent; } + const ScopeObject* parent() const { return m_parent; } + +protected: + explicit ScopeObject(ScopeObject* parent); + explicit ScopeObject(GlobalObjectTag); + + virtual void visit_edges(Visitor&) override; + +private: + ScopeObject* m_parent { nullptr }; +}; + +} diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp index c8b1d7be6f8..5b751be4236 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -47,17 +47,17 @@ static ScriptFunction* typed_this(VM& vm, GlobalObject& global_object) return static_cast(this_object); } -ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, LexicalEnvironment* parent_environment, bool is_strict, bool is_arrow_function) +ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, ScopeObject* parent_scope, bool is_strict, bool is_arrow_function) { - return global_object.heap().allocate(global_object, global_object, name, body, move(parameters), m_function_length, parent_environment, *global_object.function_prototype(), is_strict, is_arrow_function); + return global_object.heap().allocate(global_object, global_object, name, body, move(parameters), m_function_length, parent_scope, *global_object.function_prototype(), is_strict, is_arrow_function); } -ScriptFunction::ScriptFunction(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, LexicalEnvironment* parent_environment, Object& prototype, bool is_strict, bool is_arrow_function) +ScriptFunction::ScriptFunction(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, ScopeObject* parent_scope, Object& prototype, bool is_strict, bool is_arrow_function) : Function(prototype, is_arrow_function ? vm().this_value(global_object) : Value(), {}) , m_name(name) , m_body(body) , m_parameters(move(parameters)) - , m_parent_environment(parent_environment) + , m_parent_scope(parent_scope) , m_function_length(m_function_length) , m_is_strict(is_strict) , m_is_arrow_function(is_arrow_function) @@ -84,7 +84,7 @@ ScriptFunction::~ScriptFunction() void ScriptFunction::visit_edges(Visitor& visitor) { Function::visit_edges(visitor); - visitor.visit(m_parent_environment); + visitor.visit(m_parent_scope); } LexicalEnvironment* ScriptFunction::create_environment() @@ -102,11 +102,13 @@ LexicalEnvironment* ScriptFunction::create_environment() } } - auto* environment = heap().allocate(global_object(), move(variables), m_parent_environment, LexicalEnvironment::EnvironmentRecordType::Function); + auto* environment = heap().allocate(global_object(), move(variables), m_parent_scope, LexicalEnvironment::EnvironmentRecordType::Function); environment->set_home_object(home_object()); environment->set_current_function(*this); - if (m_is_arrow_function) - environment->set_new_target(m_parent_environment->new_target()); + if (m_is_arrow_function) { + if (m_parent_scope->is_lexical_environment()) + environment->set_new_target(static_cast(m_parent_scope)->new_target()); + } return environment; } @@ -144,7 +146,7 @@ Value ScriptFunction::execute_function_body() argument_value = js_undefined(); } arguments.append({ parameter.name, argument_value }); - vm.current_environment()->set(global_object(), parameter.name, { argument_value, DeclarationKind::Var }); + vm.current_scope()->put_to_scope(parameter.name, { argument_value, DeclarationKind::Var }); } return interpreter->execute_statement(global_object(), m_body, move(arguments), ScopeType::Function); diff --git a/Libraries/LibJS/Runtime/ScriptFunction.h b/Libraries/LibJS/Runtime/ScriptFunction.h index 346d6e82193..cefb2e58e9b 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.h +++ b/Libraries/LibJS/Runtime/ScriptFunction.h @@ -35,9 +35,9 @@ class ScriptFunction final : public Function { JS_OBJECT(ScriptFunction, Function); public: - static ScriptFunction* create(GlobalObject&, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, LexicalEnvironment* parent_environment, bool is_strict, bool is_arrow_function = false); + static ScriptFunction* create(GlobalObject&, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, ScopeObject* parent_scope, bool is_strict, bool is_arrow_function = false); - ScriptFunction(GlobalObject&, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, LexicalEnvironment* parent_environment, Object& prototype, bool is_strict, bool is_arrow_function = false); + ScriptFunction(GlobalObject&, const FlyString& name, const Statement& body, Vector parameters, i32 m_function_length, ScopeObject* parent_scope, Object& prototype, bool is_strict, bool is_arrow_function = false); virtual void initialize(GlobalObject&) override; virtual ~ScriptFunction(); @@ -68,7 +68,7 @@ private: FlyString m_name; NonnullRefPtr m_body; const Vector m_parameters; - LexicalEnvironment* m_parent_environment { nullptr }; + ScopeObject* m_parent_scope { nullptr }; 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 278b0c68bbc..953aae1f519 100644 --- a/Libraries/LibJS/Runtime/VM.cpp +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -51,6 +51,8 @@ VM::VM() m_single_ascii_character_strings[i] = m_heap.allocate_without_global_object(String::format("%c", i)); } + m_scope_object_shape = m_heap.allocate_without_global_object(Shape::ShapeWithoutGlobalObjectTag::Tag); + #define __JS_ENUMERATE(SymbolName, snake_name) \ m_well_known_symbol_##snake_name = js_symbol(*this, "Symbol." #SymbolName, false); JS_ENUMERATE_WELL_KNOWN_SYMBOLS @@ -103,6 +105,8 @@ void VM::gather_roots(HashTable& roots) for (auto* string : m_single_ascii_character_strings) roots.set(string); + roots.set(m_scope_object_shape); + if (m_exception) roots.set(m_exception); @@ -116,7 +120,7 @@ void VM::gather_roots(HashTable& roots) if (argument.is_cell()) roots.set(argument.as_cell()); } - roots.set(call_frame->environment); + roots.set(call_frame->scope); } #define __JS_ENUMERATE(SymbolName, snake_name) \ @@ -142,17 +146,15 @@ Symbol* VM::get_global_symbol(const String& description) void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_object, bool first_assignment) { if (m_call_stack.size()) { - for (auto* environment = current_environment(); environment; environment = environment->parent()) { - if (environment->type() == LexicalEnvironment::EnvironmentRecordType::Global) - break; - auto possible_match = environment->get(name); + for (auto* scope = current_scope(); scope; scope = scope->parent()) { + auto possible_match = scope->get_from_scope(name); if (possible_match.has_value()) { if (!first_assignment && possible_match.value().declaration_kind == DeclarationKind::Const) { throw_exception(global_object, ErrorType::InvalidAssignToConst); return; } - environment->set(global_object, name, { value, possible_match.value().declaration_kind }); + scope->put_to_scope(name, { value, possible_match.value().declaration_kind }); return; } } @@ -164,10 +166,8 @@ void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_o Value VM::get_variable(const FlyString& name, GlobalObject& global_object) { if (m_call_stack.size()) { - for (auto* environment = current_environment(); environment; environment = environment->parent()) { - if (environment->type() == LexicalEnvironment::EnvironmentRecordType::Global) - break; - auto possible_match = environment->get(name); + for (auto* scope = current_scope(); scope; scope = scope->parent()) { + auto possible_match = scope->get_from_scope(name); if (possible_match.has_value()) return possible_match.value().value; } @@ -181,10 +181,10 @@ Value VM::get_variable(const FlyString& name, GlobalObject& global_object) Reference VM::get_reference(const FlyString& name) { if (m_call_stack.size()) { - for (auto* environment = current_environment(); environment; environment = environment->parent()) { - if (environment->type() == LexicalEnvironment::EnvironmentRecordType::Global) + for (auto* scope = current_scope(); scope; scope = scope->parent()) { + if (scope->is_global_object()) break; - auto possible_match = environment->get(name); + auto possible_match = scope->get_from_scope(name); if (possible_match.has_value()) return { Reference::LocalVariable, name }; } @@ -208,13 +208,14 @@ Value VM::construct(Function& function, Function& new_target, Optionalset_new_target(&new_target); + auto* environment = function.create_environment(); + call_frame.scope = environment; + environment->set_new_target(&new_target); Object* new_object = nullptr; if (function.constructor_kind() == Function::ConstructorKind::Base) { new_object = Object::create_empty(global_object); - call_frame.environment->bind_this_value(global_object, new_object); + environment->bind_this_value(global_object, new_object); if (exception()) return {}; auto prototype = new_target.get(names.prototype); @@ -232,14 +233,15 @@ Value VM::construct(Function& function, Function& new_target, Optionalget_this_binding(global_object); + this_value = call_frame.scope->get_this_binding(global_object); pop_call_frame(); call_frame_popper.disarm(); // If we are constructing an instance of a derived class, // set the prototype on objects created by constructors that return an object (i.e. NativeFunction subclasses). if (function.constructor_kind() == Function::ConstructorKind::Base && new_target.constructor_kind() == Function::ConstructorKind::Derived && result.is_object()) { - current_environment()->replace_this_binding(result); + ASSERT(current_scope()->is_lexical_environment()); + static_cast(current_scope())->replace_this_binding(result); auto prototype = new_target.get(names.prototype); if (exception()) return {}; @@ -292,22 +294,23 @@ String VM::join_arguments() const Value VM::resolve_this_binding(GlobalObject& global_object) const { - return get_this_environment()->get_this_binding(global_object); + return find_this_scope()->get_this_binding(global_object); } -const LexicalEnvironment* VM::get_this_environment() const +const ScopeObject* VM::find_this_scope() const { // We will always return because the Global environment will always be reached, which has a |this| binding. - for (const LexicalEnvironment* environment = current_environment(); environment; environment = environment->parent()) { - if (environment->has_this_binding()) - return environment; + for (auto* scope = current_scope(); scope; scope = scope->parent()) { + if (scope->has_this_binding()) + return scope; } ASSERT_NOT_REACHED(); } Value VM::get_new_target() const { - return get_this_environment()->new_target(); + ASSERT(find_this_scope()->is_lexical_environment()); + return static_cast(find_this_scope())->new_target(); } Value VM::call_internal(Function& function, Value this_value, Optional arguments) @@ -321,10 +324,11 @@ Value VM::call_internal(Function& function, Value this_value, Optionalthis_binding_status() == LexicalEnvironment::ThisBindingStatus::Uninitialized); - call_frame.environment->bind_this_value(function.global_object(), call_frame.this_value); + ASSERT(environment->this_binding_status() == LexicalEnvironment::ThisBindingStatus::Uninitialized); + environment->bind_this_value(function.global_object(), call_frame.this_value); if (exception()) return {}; diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h index 16e1a0213d4..b2fc9b9ac75 100644 --- a/Libraries/LibJS/Runtime/VM.h +++ b/Libraries/LibJS/Runtime/VM.h @@ -59,7 +59,7 @@ struct CallFrame { FlyString function_name; Value this_value; Vector arguments; - LexicalEnvironment* environment { nullptr }; + ScopeObject* scope { nullptr }; bool is_strict_mode { false }; }; @@ -134,8 +134,8 @@ public: const Vector& call_stack() const { return m_call_stack; } Vector& call_stack() { return m_call_stack; } - const LexicalEnvironment* current_environment() const { return call_frame().environment; } - LexicalEnvironment* current_environment() { return call_frame().environment; } + const ScopeObject* current_scope() const { return call_frame().scope; } + ScopeObject* current_scope() { return call_frame().scope; } bool in_strict_mode() const; @@ -222,7 +222,7 @@ public: String join_arguments() const; Value resolve_this_binding(GlobalObject&) const; - const LexicalEnvironment* get_this_environment() const; + const ScopeObject* find_this_scope() const; Value get_new_target() const; template @@ -242,6 +242,8 @@ public: CommonPropertyNames names; + Shape& scope_object_shape() { return *m_scope_object_shape; } + private: VM(); @@ -271,6 +273,8 @@ private: Symbol* m_well_known_symbol_##snake_name { nullptr }; JS_ENUMERATE_WELL_KNOWN_SYMBOLS #undef __JS_ENUMERATE + + Shape* m_scope_object_shape { nullptr }; }; template<>