LibJS: Add a scope object abstraction

Both GlobalObject and LexicalEnvironment now inherit from ScopeObject,
and the VM's call frames point to a ScopeObject chain rather than just
a LexicalEnvironment chain.

This gives us much more flexibility to implement things like "with",
and also unifies some of the code paths that previously required
special handling of the global object.

There's a bunch of more cleanup that can be done in the wake of this
change, and there might be some oversights in the handling of the
"super" keyword, but this generally seems like a good architectural
improvement. :^)
This commit is contained in:
Andreas Kling 2020-11-28 16:02:27 +01:00
parent e1bbc7c075
commit c3fe9b4df8
Notes: sideshowbarker 2024-07-19 01:13:02 +09:00
16 changed files with 241 additions and 92 deletions

View file

@ -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<const MemberExpression&>(*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<TypeError>(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();
}

View file

@ -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

View file

@ -124,6 +124,7 @@ class NativeProperty;
class PrimitiveString;
class Reference;
class ScopeNode;
class ScopeObject;
class Shape;
class Statement;
class Symbol;

View file

@ -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<LexicalEnvironment>(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<LexicalEnvironment>(global_object, move(scope_variables_with_declaration_kind), current_environment());
vm().call_frame().environment = block_lexical_environment;
auto* block_lexical_environment = heap().allocate<LexicalEnvironment>(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<LexicalEnvironment*>(vm().call_frame().scope);
}
}

View file

@ -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&);

View file

@ -68,7 +68,7 @@
namespace JS {
GlobalObject::GlobalObject()
: Object(GlobalObjectTag::Tag)
: ScopeObject(GlobalObjectTag::Tag)
, m_console(make<Console>(*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<Variable> 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);
}
}

View file

@ -27,13 +27,13 @@
#pragma once
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/ScopeObject.h>
#include <LibJS/Runtime/VM.h>
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<Variable> 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);

View file

@ -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<FlyString, Variable> variables, LexicalEnvironment* parent)
: m_parent(parent)
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope)
: ScopeObject(parent_scope)
, m_variables(move(variables))
{
}
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, LexicalEnvironment* parent, EnvironmentRecordType environment_record_type)
: m_environment_record_type(environment_record_type)
, m_parent(parent)
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> 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<Variable> LexicalEnvironment::get(const FlyString& name) const
Optional<Variable> 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();

View file

@ -28,17 +28,14 @@
#include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <LibJS/Runtime/Cell.h>
#include <LibJS/Runtime/ScopeObject.h>
#include <LibJS/Runtime/Value.h>
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<FlyString, Variable> variables, LexicalEnvironment* parent);
LexicalEnvironment(HashMap<FlyString, Variable> variables, LexicalEnvironment* parent, EnvironmentRecordType);
LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope);
LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope, EnvironmentRecordType);
virtual ~LexicalEnvironment() override;
LexicalEnvironment* parent() const { return m_parent; }
Optional<Variable> get(const FlyString&) const;
void set(GlobalObject&, const FlyString&, Variable);
// ^ScopeObject
virtual Optional<Variable> 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<FlyString, Variable> m_variables;
Value m_home_object;
Value m_this_value;

View file

@ -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;

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibJS/Runtime/ScopeObject.h>
#include <LibJS/Runtime/VM.h>
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);
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibJS/Runtime/Object.h>
namespace JS {
struct Variable {
Value value;
DeclarationKind declaration_kind;
};
class ScopeObject : public Object {
JS_OBJECT(ScopeObject, Object);
public:
virtual Optional<Variable> 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 };
};
}

View file

@ -47,17 +47,17 @@ static ScriptFunction* typed_this(VM& vm, GlobalObject& global_object)
return static_cast<ScriptFunction*>(this_object);
}
ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> 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<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, bool is_strict, bool is_arrow_function)
{
return global_object.heap().allocate<ScriptFunction>(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<ScriptFunction>(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<FunctionNode::Parameter> 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<FunctionNode::Parameter> 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<LexicalEnvironment>(global_object(), move(variables), m_parent_environment, LexicalEnvironment::EnvironmentRecordType::Function);
auto* environment = heap().allocate<LexicalEnvironment>(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<LexicalEnvironment*>(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);

View file

@ -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<FunctionNode::Parameter> 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<FunctionNode::Parameter> 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<FunctionNode::Parameter> 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<FunctionNode::Parameter> 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<Statement> m_body;
const Vector<FunctionNode::Parameter> 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 };

View file

@ -51,6 +51,8 @@ VM::VM()
m_single_ascii_character_strings[i] = m_heap.allocate_without_global_object<PrimitiveString>(String::format("%c", i));
}
m_scope_object_shape = m_heap.allocate_without_global_object<Shape>(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<Cell*>& 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<Cell*>& 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<TypeError>(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, Optional<MarkedVal
call_frame.arguments = function.bound_arguments();
if (arguments.has_value())
call_frame.arguments.append(arguments.value().values());
call_frame.environment = function.create_environment();
call_frame.environment->set_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, Optional<MarkedVal
call_frame.this_value = this_value;
auto result = function.construct(new_target);
this_value = call_frame.environment->get_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<LexicalEnvironment*>(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<const LexicalEnvironment*>(find_this_scope())->new_target();
}
Value VM::call_internal(Function& function, Value this_value, Optional<MarkedValueList> arguments)
@ -321,10 +324,11 @@ Value VM::call_internal(Function& function, Value this_value, Optional<MarkedVal
call_frame.arguments = function.bound_arguments();
if (arguments.has_value())
call_frame.arguments.append(move(arguments.release_value().values()));
call_frame.environment = function.create_environment();
auto* environment = function.create_environment();
call_frame.scope = environment;
ASSERT(call_frame.environment->this_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 {};

View file

@ -59,7 +59,7 @@ struct CallFrame {
FlyString function_name;
Value this_value;
Vector<Value> arguments;
LexicalEnvironment* environment { nullptr };
ScopeObject* scope { nullptr };
bool is_strict_mode { false };
};
@ -134,8 +134,8 @@ public:
const Vector<CallFrame*>& call_stack() const { return m_call_stack; }
Vector<CallFrame*>& 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<typename... Args>
@ -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<>