1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 06:14:58 +00:00

LibJS: Always allocate ExecutionContext objects on the malloc heap

Instead of allocating these in a mixture of ways, we now always put
them on the malloc heap, and keep an intrusive linked list of them
that we can iterate for GC marking purposes.
This commit is contained in:
Andreas Kling 2023-11-27 16:45:45 +01:00
parent 845da3901d
commit 3dc5f467a8
38 changed files with 251 additions and 217 deletions

View file

@ -23,7 +23,7 @@ Workbook::Workbook(Vector<NonnullRefPtr<Sheet>>&& sheets, GUI::Window& parent_wi
: m_sheets(move(sheets))
, m_vm(JS::VM::create().release_value_but_fixme_should_propagate_errors())
, m_root_execution_context(JS::create_simple_execution_context<JS::GlobalObject>(m_vm))
, m_main_execution_context(m_vm->heap())
, m_main_execution_context(JS::ExecutionContext::create(m_vm->heap()))
, m_parent_window(parent_window)
{
auto& realm = *m_root_execution_context->realm;
@ -31,13 +31,13 @@ Workbook::Workbook(Vector<NonnullRefPtr<Sheet>>&& sheets, GUI::Window& parent_wi
m_workbook_object = vm.heap().allocate<WorkbookObject>(realm, realm, *this);
realm.global_object().define_direct_property("workbook", workbook_object(), JS::default_attributes);
m_main_execution_context.this_value = &realm.global_object();
m_main_execution_context.function_name = "(global execution context)"sv;
m_main_execution_context.lexical_environment = &realm.global_environment();
m_main_execution_context.variable_environment = &realm.global_environment();
m_main_execution_context.realm = &realm;
m_main_execution_context.is_strict_mode = true;
m_vm->push_execution_context(m_main_execution_context);
m_main_execution_context->this_value = &realm.global_object();
m_main_execution_context->function_name = JS::PrimitiveString::create(vm, "(global execution context)"sv);
m_main_execution_context->lexical_environment = &realm.global_environment();
m_main_execution_context->variable_environment = &realm.global_environment();
m_main_execution_context->realm = &realm;
m_main_execution_context->is_strict_mode = true;
m_vm->push_execution_context(*m_main_execution_context);
m_vm->enable_default_host_import_module_dynamically_hook();
}

View file

@ -47,7 +47,7 @@ private:
NonnullOwnPtr<JS::ExecutionContext> m_root_execution_context;
JS::GCPtr<WorkbookObject> m_workbook_object;
JS::ExecutionContext m_main_execution_context;
NonnullOwnPtr<JS::ExecutionContext> m_main_execution_context;
GUI::Window& m_parent_window;
DeprecatedString m_current_filename;

View file

@ -1666,7 +1666,7 @@ void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment
// iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
if (function_declaration.name_identifier()->is_local()) {
vm.running_execution_context().local_variables[function_declaration.name_identifier()->local_variable_index()] = function;
vm.running_execution_context().local(function_declaration.name_identifier()->local_variable_index()) = function;
} else {
VERIFY(is<DeclarativeEnvironment>(*environment));
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function_declaration.name(), function);

View file

@ -65,36 +65,36 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Envir
auto& global_environment = script_record.realm().global_environment();
// 2. Let scriptContext be a new ECMAScript code execution context.
ExecutionContext script_context(vm.heap());
auto script_context = ExecutionContext::create(vm.heap());
// 3. Set the Function of scriptContext to null.
// NOTE: This was done during execution context construction.
// 4. Set the Realm of scriptContext to scriptRecord.[[Realm]].
script_context.realm = &script_record.realm();
script_context->realm = &script_record.realm();
// 5. Set the ScriptOrModule of scriptContext to scriptRecord.
script_context.script_or_module = NonnullGCPtr<Script>(script_record);
script_context->script_or_module = NonnullGCPtr<Script>(script_record);
// 6. Set the VariableEnvironment of scriptContext to globalEnv.
script_context.variable_environment = &global_environment;
script_context->variable_environment = &global_environment;
// 7. Set the LexicalEnvironment of scriptContext to globalEnv.
script_context.lexical_environment = &global_environment;
script_context->lexical_environment = &global_environment;
// Non-standard: Override the lexical environment if requested.
if (lexical_environment_override)
script_context.lexical_environment = lexical_environment_override;
script_context->lexical_environment = lexical_environment_override;
// 8. Set the PrivateEnvironment of scriptContext to null.
// NOTE: This isn't in the spec, but we require it.
script_context.is_strict_mode = script_record.parse_node().is_strict_mode();
script_context->is_strict_mode = script_record.parse_node().is_strict_mode();
// FIXME: 9. Suspend the currently running execution context.
// 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context.
TRY(vm.push_execution_context(script_context, {}));
TRY(vm.push_execution_context(*script_context, {}));
// 11. Let script be scriptRecord.[[ECMAScriptCode]].
auto& script = script_record.parse_node();
@ -183,7 +183,7 @@ ThrowCompletionOr<Value> Interpreter::run(SourceTextModule& module)
void Interpreter::run_bytecode()
{
auto* locals = vm().running_execution_context().local_variables.data();
auto* locals = vm().running_execution_context().locals.data();
auto* registers = this->registers().data();
auto& accumulator = this->accumulator();
for (;;) {
@ -1269,7 +1269,7 @@ ThrowCompletionOr<void> TypeofVariable::execute_impl(Bytecode::Interpreter& inte
ThrowCompletionOr<void> TypeofLocal::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto const& value = vm.running_execution_context().local_variables[m_index];
auto const& value = vm.running_execution_context().local(m_index);
interpreter.accumulator() = PrimitiveString::create(vm, value.typeof());
return {};
}

View file

@ -450,6 +450,9 @@ void Heap::mark_live_cells(HashMap<Cell*, HeapRoot> const& roots)
MarkingVisitor visitor(*this, roots);
for (auto& execution_context : m_execution_contexts)
execution_context.visit_edges(visitor);
vm().bytecode_interpreter().visit_edges(visitor);
visitor.mark_all_live_cells();

View file

@ -23,6 +23,7 @@
#include <LibJS/Heap/Internals.h>
#include <LibJS/Heap/MarkedVector.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/ExecutionContext.h>
#include <LibJS/Runtime/WeakContainer.h>
namespace JS {
@ -77,6 +78,9 @@ public:
void did_create_weak_container(Badge<WeakContainer>, WeakContainer&);
void did_destroy_weak_container(Badge<WeakContainer>, WeakContainer&);
void did_create_execution_context(Badge<ExecutionContext>, ExecutionContext&);
void did_destroy_execution_context(Badge<ExecutionContext>, ExecutionContext&);
BlockAllocator& block_allocator() { return m_block_allocator; }
void uproot_cell(Cell* cell);
@ -144,6 +148,7 @@ private:
HandleImpl::List m_handles;
MarkedVectorBase::List m_marked_vectors;
WeakContainer::List m_weak_containers;
ExecutionContext::List m_execution_contexts;
Vector<GCPtr<Cell>> m_uprooted_cells;
@ -191,4 +196,16 @@ inline void Heap::did_destroy_weak_container(Badge<WeakContainer>, WeakContainer
m_weak_containers.remove(set);
}
inline void Heap::did_create_execution_context(Badge<ExecutionContext>, ExecutionContext& set)
{
VERIFY(!m_execution_contexts.contains(set));
m_execution_contexts.append(set);
}
inline void Heap::did_destroy_execution_context(Badge<ExecutionContext>, ExecutionContext& set)
{
VERIFY(m_execution_contexts.contains(set));
m_execution_contexts.remove(set);
}
}

View file

@ -46,7 +46,7 @@ void NativeExecutable::run(VM& vm, size_t entry_point) const
typedef void (*JITCode)(VM&, Value* registers, Value* locals, FlatPtr entry_point_address, ExecutionContext&);
((JITCode)m_code)(vm,
vm.bytecode_interpreter().registers().data(),
vm.running_execution_context().local_variables.data(),
vm.running_execution_context().locals.data(),
entry_point_address,
vm.running_execution_context());
}

View file

@ -638,31 +638,31 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
// FIXME: We don't have this concept yet.
// 20. Let evalContext be a new ECMAScript code execution context.
ExecutionContext eval_context(vm.heap());
auto eval_context = ExecutionContext::create(vm.heap());
// 21. Set evalContext's Function to null.
// NOTE: This was done in the construction of eval_context.
// 22. Set evalContext's Realm to evalRealm.
eval_context.realm = &eval_realm;
eval_context->realm = &eval_realm;
// 23. Set evalContext's ScriptOrModule to runningContext's ScriptOrModule.
eval_context.script_or_module = running_context.script_or_module;
eval_context->script_or_module = running_context.script_or_module;
// 24. Set evalContext's VariableEnvironment to varEnv.
eval_context.variable_environment = variable_environment;
eval_context->variable_environment = variable_environment;
// 25. Set evalContext's LexicalEnvironment to lexEnv.
eval_context.lexical_environment = lexical_environment;
eval_context->lexical_environment = lexical_environment;
// 26. Set evalContext's PrivateEnvironment to privateEnv.
eval_context.private_environment = private_environment;
eval_context->private_environment = private_environment;
// NOTE: This isn't in the spec, but we require it.
eval_context.is_strict_mode = strict_eval;
eval_context->is_strict_mode = strict_eval;
// 27. Push evalContext onto the execution context stack; evalContext is now the running execution context.
TRY(vm.push_execution_context(eval_context, {}));
TRY(vm.push_execution_context(*eval_context, {}));
// NOTE: We use a ScopeGuard to automatically pop the execution context when any of the `TRY`s below return a throw completion.
ScopeGuard pop_guard = [&] {
@ -1025,7 +1025,7 @@ ThrowCompletionOr<void> eval_declaration_instantiation(VM& vm, Program const& pr
}
// 10.4.4.6 CreateUnmappedArgumentsObject ( argumentsList ), https://tc39.es/ecma262/#sec-createunmappedargumentsobject
Object* create_unmapped_arguments_object(VM& vm, Span<Value> arguments)
Object* create_unmapped_arguments_object(VM& vm, ReadonlySpan<Value> arguments)
{
auto& realm = *vm.current_realm();
@ -1065,7 +1065,7 @@ Object* create_unmapped_arguments_object(VM& vm, Span<Value> arguments)
}
// 10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ), https://tc39.es/ecma262/#sec-createmappedargumentsobject
Object* create_mapped_arguments_object(VM& vm, FunctionObject& function, Vector<FunctionParameter> const& formals, Span<Value> arguments, Environment& environment)
Object* create_mapped_arguments_object(VM& vm, FunctionObject& function, Vector<FunctionParameter> const& formals, ReadonlySpan<Value> arguments, Environment& environment)
{
auto& realm = *vm.current_realm();

View file

@ -39,8 +39,8 @@ ThrowCompletionOr<void> initialize_bound_name(VM&, DeprecatedFlyString const&, V
bool is_compatible_property_descriptor(bool extensible, PropertyDescriptor const&, Optional<PropertyDescriptor> const& current);
bool validate_and_apply_property_descriptor(Object*, PropertyKey const&, bool extensible, PropertyDescriptor const&, Optional<PropertyDescriptor> const& current);
ThrowCompletionOr<Object*> get_prototype_from_constructor(VM&, FunctionObject const& constructor, NonnullGCPtr<Object> (Intrinsics::*intrinsic_default_prototype)());
Object* create_unmapped_arguments_object(VM&, Span<Value> arguments);
Object* create_mapped_arguments_object(VM&, FunctionObject&, Vector<FunctionParameter> const&, Span<Value> arguments, Environment&);
Object* create_unmapped_arguments_object(VM&, ReadonlySpan<Value> arguments);
Object* create_mapped_arguments_object(VM&, FunctionObject&, Vector<FunctionParameter> const&, ReadonlySpan<Value> arguments, Environment&);
struct DisposableResource {
Value resource_value;

View file

@ -45,7 +45,9 @@ ThrowCompletionOr<NonnullGCPtr<Object>> AsyncFunctionConstructor::construct(Func
auto* constructor = vm.active_function_object();
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
auto& args = vm.running_execution_context().arguments;
MarkedVector<Value> args(heap());
for (auto argument : vm.running_execution_context().arguments)
args.append(argument);
// 3. Return CreateDynamicFunction(C, NewTarget, async, args).
return *TRY(FunctionConstructor::create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Async, args));

View file

@ -45,7 +45,8 @@ ThrowCompletionOr<void> AsyncFunctionDriverWrapper::await(JS::Value value)
auto& realm = *vm.current_realm();
// 1. Let asyncContext be the running execution context.
m_suspended_execution_context = vm.running_execution_context().copy();
if (!m_suspended_execution_context)
m_suspended_execution_context = vm.running_execution_context().copy();
// 2. Let promise be ? PromiseResolve(%Promise%, value).
auto* promise_object = TRY(promise_resolve(vm, realm.intrinsics().promise_constructor(), value));
@ -61,7 +62,7 @@ ThrowCompletionOr<void> AsyncFunctionDriverWrapper::await(JS::Value value)
// FIXME: b. Suspend prevContext.
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
TRY(vm.push_execution_context(m_suspended_execution_context.value(), {}));
TRY(vm.push_execution_context(*m_suspended_execution_context, {}));
// d. Resume the suspended evaluation of asyncContext using NormalCompletion(v) as the result of the operation that
// suspended it.
@ -89,7 +90,7 @@ ThrowCompletionOr<void> AsyncFunctionDriverWrapper::await(JS::Value value)
// FIXME: b. Suspend prevContext.
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
TRY(vm.push_execution_context(m_suspended_execution_context.value(), {}));
TRY(vm.push_execution_context(*m_suspended_execution_context, {}));
// d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that
// suspended it.
@ -191,8 +192,6 @@ void AsyncFunctionDriverWrapper::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_top_level_promise);
if (m_current_promise)
visitor.visit(m_current_promise);
if (m_suspended_execution_context.has_value())
m_suspended_execution_context->visit_edges(visitor);
}
}

View file

@ -39,7 +39,7 @@ private:
NonnullGCPtr<Promise> m_top_level_promise;
GCPtr<Promise> m_current_promise { nullptr };
Handle<AsyncFunctionDriverWrapper> m_self_handle;
Optional<ExecutionContext> m_suspended_execution_context;
OwnPtr<ExecutionContext> m_suspended_execution_context;
};
}

View file

@ -16,7 +16,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(AsyncGenerator);
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, ExecutionContext execution_context, Bytecode::CallFrame frame)
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, Bytecode::CallFrame frame)
{
auto& vm = realm.vm();
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
@ -29,7 +29,7 @@ ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& re
return object;
}
AsyncGenerator::AsyncGenerator(Realm&, Object& prototype, ExecutionContext context)
AsyncGenerator::AsyncGenerator(Realm&, Object& prototype, NonnullOwnPtr<ExecutionContext> context)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_async_generator_context(move(context))
{
@ -43,7 +43,6 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
visitor.visit(*request.completion.value());
visitor.visit(request.capability);
}
m_async_generator_context.visit_edges(visitor);
visitor.visit(m_generating_function);
visitor.visit(m_previous_value);
if (m_frame.has_value())
@ -328,7 +327,7 @@ ThrowCompletionOr<void> AsyncGenerator::resume(VM& vm, Completion completion)
m_async_generator_state = State::Executing;
// 6. Push genContext onto the execution context stack; genContext is now the running execution context.
TRY(vm.push_execution_context(generator_context, {}));
TRY(vm.push_execution_context(*generator_context, {}));
// 7. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended
// it. Let result be the Completion Record returned by the resumed computation.

View file

@ -28,7 +28,7 @@ public:
Completed,
};
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, ExecutionContext, Bytecode::CallFrame);
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, Bytecode::CallFrame);
virtual ~AsyncGenerator() override = default;
@ -44,7 +44,7 @@ public:
Optional<String> const& generator_brand() const { return m_generator_brand; }
private:
AsyncGenerator(Realm&, Object& prototype, ExecutionContext);
AsyncGenerator(Realm&, Object& prototype, NonnullOwnPtr<ExecutionContext>);
virtual void visit_edges(Cell::Visitor&) override;
@ -53,10 +53,10 @@ private:
// At the time of constructing an AsyncGenerator, we still need to point to an
// execution context on the stack, but later need to 'adopt' it.
State m_async_generator_state { State::SuspendedStart }; // [[AsyncGeneratorState]]
ExecutionContext m_async_generator_context; // [[AsyncGeneratorContext]]
Vector<AsyncGeneratorRequest> m_async_generator_queue; // [[AsyncGeneratorQueue]]
Optional<String> m_generator_brand; // [[GeneratorBrand]]
State m_async_generator_state { State::SuspendedStart }; // [[AsyncGeneratorState]]
NonnullOwnPtr<ExecutionContext> m_async_generator_context; // [[AsyncGeneratorContext]]
Vector<AsyncGeneratorRequest> m_async_generator_queue; // [[AsyncGeneratorQueue]]
Optional<String> m_generator_brand; // [[GeneratorBrand]]
GCPtr<ECMAScriptFunctionObject> m_generating_function;
Value m_previous_value;

View file

@ -46,7 +46,9 @@ ThrowCompletionOr<NonnullGCPtr<Object>> AsyncGeneratorFunctionConstructor::const
auto* constructor = vm.active_function_object();
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
auto& args = vm.running_execution_context().arguments;
MarkedVector<Value> args(heap());
for (auto argument : vm.running_execution_context().arguments)
args.append(argument);
// 3. Return ? CreateDynamicFunction(C, NewTarget, asyncGenerator, args).
return *TRY(FunctionConstructor::create_dynamic_function(vm, *constructor, &new_target, FunctionKind::AsyncGenerator, args));

View file

@ -370,20 +370,20 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
// 1. Let callerContext be the running execution context.
// NOTE: No-op, kept by the VM in its execution context stack.
ExecutionContext callee_context(heap());
auto callee_context = ExecutionContext::create(heap());
callee_context.local_variables.resize(m_local_variables_names.size());
callee_context->locals.resize(m_local_variables_names.size());
// Non-standard
callee_context.arguments.append(arguments_list.data(), arguments_list.size());
callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
callee_context->arguments.append(arguments_list.data(), arguments_list.size());
callee_context->instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
// 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
// NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check.
TRY(prepare_for_ordinary_call(callee_context, nullptr));
TRY(prepare_for_ordinary_call(*callee_context, nullptr));
// 3. Assert: calleeContext is now the running execution context.
VERIFY(&vm.running_execution_context() == &callee_context);
VERIFY(&vm.running_execution_context() == callee_context);
// 4. If F.[[IsClassConstructor]] is true, then
if (m_is_class_constructor) {
@ -399,7 +399,7 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
}
// 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
ordinary_call_bind_this(callee_context, this_argument);
ordinary_call_bind_this(*callee_context, this_argument);
// 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)).
auto result = ordinary_call_evaluate_body();
@ -440,25 +440,25 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ECMAScriptFunctionObject::internal_const
this_argument = TRY(ordinary_create_from_constructor<Object>(vm, new_target, &Intrinsics::object_prototype, ConstructWithPrototypeTag::Tag));
}
ExecutionContext callee_context(heap());
auto callee_context = ExecutionContext::create(heap());
callee_context.local_variables.resize(m_local_variables_names.size());
callee_context->locals.resize(m_local_variables_names.size());
// Non-standard
callee_context.arguments.append(arguments_list.data(), arguments_list.size());
callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
callee_context->arguments.append(arguments_list.data(), arguments_list.size());
callee_context->instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
// 4. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
// NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check.
TRY(prepare_for_ordinary_call(callee_context, &new_target));
TRY(prepare_for_ordinary_call(*callee_context, &new_target));
// 5. Assert: calleeContext is now the running execution context.
VERIFY(&vm.running_execution_context() == &callee_context);
VERIFY(&vm.running_execution_context() == callee_context);
// 6. If kind is base, then
if (kind == ConstructorKind::Base) {
// a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
ordinary_call_bind_this(callee_context, this_argument);
ordinary_call_bind_this(*callee_context, this_argument);
// b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)).
auto initialize_result = this_argument->initialize_instance_elements(*this);
@ -474,7 +474,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ECMAScriptFunctionObject::internal_const
}
// 7. Let constructorEnv be the LexicalEnvironment of calleeContext.
auto constructor_env = callee_context.lexical_environment;
auto constructor_env = callee_context->lexical_environment;
// 8. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)).
auto result = ordinary_call_evaluate_body();
@ -681,7 +681,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
// 26. Else,
// a. Perform ? IteratorBindingInitialization of formals with arguments iteratorRecord and env.
// NOTE: The spec makes an iterator here to do IteratorBindingInitialization but we just do it manually
auto& execution_context_arguments = vm.running_execution_context().arguments;
auto execution_context_arguments = vm.running_execution_context().arguments;
size_t default_parameter_index = 0;
for (size_t i = 0; i < m_formal_parameters.size(); ++i) {
@ -713,7 +713,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
if constexpr (IsSame<NonnullRefPtr<Identifier const> const&, decltype(param)>) {
if (param->is_local()) {
callee_context.local_variables[param->local_variable_index()] = argument_value;
callee_context.locals[param->local_variable_index()] = argument_value;
return {};
}
Reference reference = TRY(vm.resolve_binding(param->string(), used_environment));
@ -749,7 +749,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
// i. If instantiatedVarNames does not contain n, then
// 1. Append n to instantiatedVarNames.
if (id.is_local()) {
callee_context.local_variables[id.local_variable_index()] = js_undefined();
callee_context.locals[id.local_variable_index()] = js_undefined();
} else {
// 2. Perform ! env.CreateMutableBinding(n, false).
// 3. Perform ! env.InitializeBinding(n, undefined).
@ -804,7 +804,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
else {
// a. Let initialValue be ! env.GetBindingValue(n, false).
if (id.is_local()) {
initial_value = callee_context.local_variables[id.local_variable_index()];
initial_value = callee_context.locals[id.local_variable_index()];
} else {
initial_value = MUST(environment->get_binding_value(vm, id.string(), false));
}
@ -813,7 +813,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
// 5. Perform ! varEnv.InitializeBinding(n, initialValue).
if (id.is_local()) {
// NOTE: Local variables are supported only in bytecode interpreter
callee_context.local_variables[id.local_variable_index()] = initial_value;
callee_context.locals[id.local_variable_index()] = initial_value;
} else {
MUST(var_environment->initialize_binding(vm, id.string(), initial_value, Environment::InitializeBindingHint::Normal));
}
@ -906,7 +906,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
// c. Perform ! varEnv.SetMutableBinding(fn, fo, false).
if (declaration.name_identifier()->is_local()) {
callee_context.local_variables[declaration.name_identifier()->local_variable_index()] = function;
callee_context.locals[declaration.name_identifier()->local_variable_index()] = function;
} else {
MUST(var_environment->set_mutable_binding(vm, declaration.name(), function, false));
}
@ -1056,7 +1056,7 @@ void async_function_start(VM& vm, PromiseCapability const& promise_capability, T
// 3. NOTE: Copying the execution state is required for AsyncBlockStart to resume its execution. It is ill-defined to resume a currently executing context.
// 4. Perform AsyncBlockStart(promiseCapability, asyncFunctionBody, asyncContext).
async_block_start(vm, async_function_body, promise_capability, async_context);
async_block_start(vm, async_function_body, promise_capability, *async_context);
// 5. Return unused.
}

View file

@ -7,38 +7,44 @@
*/
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/ExecutionContext.h>
#include <LibJS/Runtime/FunctionObject.h>
namespace JS {
NonnullOwnPtr<ExecutionContext> ExecutionContext::create(Heap& heap)
{
return adopt_own(*new ExecutionContext(heap));
}
ExecutionContext::ExecutionContext(Heap& heap)
: arguments(heap)
, local_variables(heap)
: m_heap(heap)
{
m_heap.did_create_execution_context({}, *this);
}
ExecutionContext::ExecutionContext(MarkedVector<Value> existing_arguments, MarkedVector<Value> existing_local_variables)
: arguments(move(existing_arguments))
, local_variables(move(existing_local_variables))
ExecutionContext::~ExecutionContext()
{
m_heap.did_destroy_execution_context({}, *this);
}
ExecutionContext ExecutionContext::copy() const
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
{
ExecutionContext copy { arguments, local_variables };
copy.function = function;
copy.realm = realm;
copy.script_or_module = script_or_module;
copy.lexical_environment = lexical_environment;
copy.variable_environment = variable_environment;
copy.private_environment = private_environment;
copy.instruction_stream_iterator = instruction_stream_iterator;
copy.function_name = function_name;
copy.this_value = this_value;
copy.is_strict_mode = is_strict_mode;
auto copy = create(m_heap);
copy->function = function;
copy->realm = realm;
copy->script_or_module = script_or_module;
copy->lexical_environment = lexical_environment;
copy->variable_environment = variable_environment;
copy->private_environment = private_environment;
copy->instruction_stream_iterator = instruction_stream_iterator;
copy->function_name = function_name;
copy->this_value = this_value;
copy->is_strict_mode = is_strict_mode;
copy->executable = executable;
copy->arguments = arguments;
copy->locals = locals;
return copy;
}
@ -51,9 +57,14 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
visitor.visit(private_environment);
visitor.visit(context_owner);
visitor.visit(this_value);
visitor.visit(executable);
if (instruction_stream_iterator.has_value())
visitor.visit(const_cast<Bytecode::Executable*>(instruction_stream_iterator.value().executable()));
visitor.visit(function_name);
for (auto argument : arguments)
visitor.visit(argument);
for (auto local : locals)
visitor.visit(local);
script_or_module.visit(
[](Empty) {},
[&](auto& script_or_module) {

View file

@ -12,7 +12,6 @@
#include <AK/WeakPtr.h>
#include <LibJS/Bytecode/Instruction.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/MarkedVector.h>
#include <LibJS/Module.h>
#include <LibJS/Runtime/PrivateEnvironment.h>
#include <LibJS/Runtime/Value.h>
@ -24,9 +23,10 @@ using ScriptOrModule = Variant<Empty, NonnullGCPtr<Script>, NonnullGCPtr<Module>
// 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts
struct ExecutionContext {
explicit ExecutionContext(Heap& heap);
static NonnullOwnPtr<ExecutionContext> create(Heap&);
[[nodiscard]] NonnullOwnPtr<ExecutionContext> copy() const;
[[nodiscard]] ExecutionContext copy() const;
~ExecutionContext();
void visit_edges(Cell::Visitor&);
@ -35,9 +35,15 @@ struct ExecutionContext {
static FlatPtr variable_environment_offset() { return OFFSET_OF(ExecutionContext, variable_environment); }
private:
explicit ExecutionContext(MarkedVector<Value> existing_arguments, MarkedVector<Value> existing_local_variables);
ExecutionContext(Heap&);
IntrusiveListNode<ExecutionContext> m_list_node;
public:
Heap& m_heap;
using List = IntrusiveList<&ExecutionContext::m_list_node>;
GCPtr<FunctionObject> function; // [[Function]]
GCPtr<Realm> realm; // [[Realm]]
ScriptOrModule script_or_module; // [[ScriptOrModule]]
@ -51,8 +57,6 @@ public:
Optional<Bytecode::InstructionStreamIterator> instruction_stream_iterator;
GCPtr<PrimitiveString> function_name;
Value this_value;
MarkedVector<Value> arguments;
MarkedVector<Value> local_variables;
bool is_strict_mode { false };
GCPtr<Bytecode::Executable> executable;
@ -60,6 +64,21 @@ public:
// https://html.spec.whatwg.org/multipage/webappapis.html#skip-when-determining-incumbent-counter
// FIXME: Move this out of LibJS (e.g. by using the CustomData concept), as it's used exclusively by LibWeb.
size_t skip_when_determining_incumbent_counter { 0 };
Value argument(size_t index) const
{
if (index >= arguments.size()) [[unlikely]]
return js_undefined();
return arguments[index];
}
Value& local(size_t index)
{
return locals[index];
}
Vector<Value> arguments;
Vector<Value> locals;
};
struct StackTraceElement {

View file

@ -268,7 +268,9 @@ ThrowCompletionOr<NonnullGCPtr<Object>> FunctionConstructor::construct(FunctionO
auto* constructor = vm.active_function_object();
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
auto& args = vm.running_execution_context().arguments;
MarkedVector<Value> args(heap());
for (auto argument : vm.running_execution_context().arguments)
args.append(argument);
// 3. Return ? CreateDynamicFunction(C, NewTarget, normal, args).
return *TRY(create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Normal, args));

View file

@ -98,8 +98,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind)
Vector<Value> arguments;
if (vm.argument_count() > 1) {
arguments = vm.running_execution_context().arguments;
arguments.remove(0);
arguments.append(vm.running_execution_context().arguments.span().slice(1).data(), vm.argument_count() - 1);
}
// 3. Let F be ? BoundFunctionCreate(Target, thisArg, args).

View file

@ -44,7 +44,9 @@ ThrowCompletionOr<NonnullGCPtr<Object>> GeneratorFunctionConstructor::construct(
auto* constructor = vm.active_function_object();
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
auto& args = vm.running_execution_context().arguments;
MarkedVector<Value> args(heap());
for (auto argument : vm.running_execution_context().arguments)
args.append(argument);
// 3. Return ? CreateDynamicFunction(C, NewTarget, generator, args).
return *TRY(FunctionConstructor::create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Generator, args));

View file

@ -16,7 +16,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(GeneratorObject);
ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, ExecutionContext execution_context, Bytecode::CallFrame frame)
ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, Bytecode::CallFrame frame)
{
auto& vm = realm.vm();
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
@ -37,7 +37,7 @@ ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm&
return object;
}
GeneratorObject::GeneratorObject(Realm&, Object& prototype, ExecutionContext context, Optional<StringView> generator_brand)
GeneratorObject::GeneratorObject(Realm&, Object& prototype, NonnullOwnPtr<ExecutionContext> context, Optional<StringView> generator_brand)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_execution_context(move(context))
, m_generator_brand(move(generator_brand))
@ -49,7 +49,6 @@ void GeneratorObject::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_generating_function);
visitor.visit(m_previous_value);
m_execution_context.visit_edges(visitor);
if (m_frame.has_value())
m_frame->visit_edges(visitor);
}
@ -166,7 +165,7 @@ ThrowCompletionOr<Value> GeneratorObject::resume(VM& vm, Value value, Optional<S
// 8. Push genContext onto the execution context stack; genContext is now the running execution context.
// NOTE: This is done out of order as to not permanently disable the generator if push_execution_context throws,
// as `resume` will immediately throw when [[GeneratorState]] is "executing", never allowing the state to change.
TRY(vm.push_execution_context(generator_context, {}));
TRY(vm.push_execution_context(*generator_context, {}));
// 7. Set generator.[[GeneratorState]] to executing.
m_generator_state = GeneratorState::Executing;
@ -228,7 +227,7 @@ ThrowCompletionOr<Value> GeneratorObject::resume_abrupt(JS::VM& vm, JS::Completi
// 9. Push genContext onto the execution context stack; genContext is now the running execution context.
// NOTE: This is done out of order as to not permanently disable the generator if push_execution_context throws,
// as `resume_abrupt` will immediately throw when [[GeneratorState]] is "executing", never allowing the state to change.
TRY(vm.push_execution_context(generator_context, {}));
TRY(vm.push_execution_context(*generator_context, {}));
// 8. Set generator.[[GeneratorState]] to executing.
m_generator_state = GeneratorState::Executing;

View file

@ -17,7 +17,7 @@ class GeneratorObject : public Object {
JS_DECLARE_ALLOCATOR(GeneratorObject);
public:
static ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> create(Realm&, Value, ECMAScriptFunctionObject*, ExecutionContext, Bytecode::CallFrame);
static ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, Bytecode::CallFrame);
virtual ~GeneratorObject() override = default;
void visit_edges(Cell::Visitor&) override;
@ -34,13 +34,13 @@ public:
void set_generator_state(GeneratorState generator_state) { m_generator_state = generator_state; }
protected:
GeneratorObject(Realm&, Object& prototype, ExecutionContext, Optional<StringView> generator_brand = {});
GeneratorObject(Realm&, Object& prototype, NonnullOwnPtr<ExecutionContext>, Optional<StringView> generator_brand = {});
ThrowCompletionOr<GeneratorState> validate(VM&, Optional<StringView> const& generator_brand);
virtual ThrowCompletionOr<Value> execute(VM&, JS::Completion const& completion);
private:
ExecutionContext m_execution_context;
NonnullOwnPtr<ExecutionContext> m_execution_context;
GCPtr<ECMAScriptFunctionObject> m_generating_function;
Value m_previous_value;
Optional<Bytecode::CallFrame> m_frame;

View file

@ -121,11 +121,11 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(Value this_argument, Read
// NOTE: We don't support this concept yet.
// 3. Let calleeContext be a new execution context.
ExecutionContext callee_context(heap());
auto callee_context = ExecutionContext::create(heap());
// 4. Set the Function of calleeContext to F.
callee_context.function = this;
callee_context.function_name = m_name_string;
callee_context->function = this;
callee_context->function_name = m_name_string;
// 5. Let calleeRealm be F.[[Realm]].
auto callee_realm = m_realm;
@ -139,29 +139,29 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(Value this_argument, Read
VERIFY(callee_realm);
// 6. Set the Realm of calleeContext to calleeRealm.
callee_context.realm = callee_realm;
callee_context->realm = callee_realm;
// 7. Set the ScriptOrModule of calleeContext to null.
// Note: This is already the default value.
// 8. Perform any necessary implementation-defined initialization of calleeContext.
callee_context.this_value = this_argument;
callee_context.arguments.append(arguments_list.data(), arguments_list.size());
callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
callee_context->this_value = this_argument;
callee_context->arguments.append(arguments_list.data(), arguments_list.size());
callee_context->instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
callee_context.lexical_environment = caller_context.lexical_environment;
callee_context.variable_environment = caller_context.variable_environment;
callee_context->lexical_environment = caller_context.lexical_environment;
callee_context->variable_environment = caller_context.variable_environment;
// Note: Keeping the private environment is probably only needed because of async methods in classes
// calling async_block_start which goes through a NativeFunction here.
callee_context.private_environment = caller_context.private_environment;
callee_context->private_environment = caller_context.private_environment;
// NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
callee_context.is_strict_mode = vm.in_strict_mode();
callee_context->is_strict_mode = vm.in_strict_mode();
// </8.> --------------------------------------------------------------------------
// 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
TRY(vm.push_execution_context(callee_context, {}));
TRY(vm.push_execution_context(*callee_context, {}));
// 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F. thisArgument is the this value, argumentsList provides the named parameters, and the NewTarget value is undefined.
auto result = call();
@ -185,11 +185,11 @@ ThrowCompletionOr<NonnullGCPtr<Object>> NativeFunction::internal_construct(Reado
// NOTE: We don't support this concept yet.
// 3. Let calleeContext be a new execution context.
ExecutionContext callee_context(heap());
auto callee_context = ExecutionContext::create(heap());
// 4. Set the Function of calleeContext to F.
callee_context.function = this;
callee_context.function_name = m_name_string;
callee_context->function = this;
callee_context->function_name = m_name_string;
// 5. Let calleeRealm be F.[[Realm]].
auto callee_realm = m_realm;
@ -203,25 +203,25 @@ ThrowCompletionOr<NonnullGCPtr<Object>> NativeFunction::internal_construct(Reado
VERIFY(callee_realm);
// 6. Set the Realm of calleeContext to calleeRealm.
callee_context.realm = callee_realm;
callee_context->realm = callee_realm;
// 7. Set the ScriptOrModule of calleeContext to null.
// Note: This is already the default value.
// 8. Perform any necessary implementation-defined initialization of calleeContext.
callee_context.arguments.append(arguments_list.data(), arguments_list.size());
callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
callee_context->arguments.append(arguments_list.data(), arguments_list.size());
callee_context->instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator();
callee_context.lexical_environment = caller_context.lexical_environment;
callee_context.variable_environment = caller_context.variable_environment;
callee_context->lexical_environment = caller_context.lexical_environment;
callee_context->variable_environment = caller_context.variable_environment;
// NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
callee_context.is_strict_mode = vm.in_strict_mode();
callee_context->is_strict_mode = vm.in_strict_mode();
// </8.> --------------------------------------------------------------------------
// 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
TRY(vm.push_execution_context(callee_context, {}));
TRY(vm.push_execution_context(*callee_context, {}));
// 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F. The this value is uninitialized, argumentsList provides the named parameters, and newTarget provides the NewTarget value.
auto result = construct(new_target);

View file

@ -42,7 +42,7 @@ ThrowCompletionOr<NonnullOwnPtr<ExecutionContext>> Realm::initialize_host_define
auto realm = MUST_OR_THROW_OOM(Realm::create(vm));
// 2. Let newContext be a new execution context.
auto new_context = make<ExecutionContext>(vm.heap());
auto new_context = ExecutionContext::create(vm.heap());
// 3. Set the Function of newContext to null.
new_context->function = nullptr;

View file

@ -22,7 +22,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(ShadowRealm);
ShadowRealm::ShadowRealm(Realm& shadow_realm, ExecutionContext execution_context, Object& prototype)
ShadowRealm::ShadowRealm(Realm& shadow_realm, NonnullOwnPtr<ExecutionContext> execution_context, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_shadow_realm(shadow_realm)
, m_execution_context(move(execution_context))
@ -143,28 +143,28 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
// NOTE: We don't support this concept yet.
// 9. Let evalContext be a new ECMAScript code execution context.
auto eval_context = ExecutionContext { vm.heap() };
auto eval_context = ExecutionContext::create(vm.heap());
// 10. Set evalContext's Function to null.
eval_context.function = nullptr;
eval_context->function = nullptr;
// 11. Set evalContext's Realm to evalRealm.
eval_context.realm = &eval_realm;
eval_context->realm = &eval_realm;
// 12. Set evalContext's ScriptOrModule to null.
// Note: This is already the default value.
// 13. Set evalContext's VariableEnvironment to varEnv.
eval_context.variable_environment = variable_environment;
eval_context->variable_environment = variable_environment;
// 14. Set evalContext's LexicalEnvironment to lexEnv.
eval_context.lexical_environment = lexical_environment;
eval_context->lexical_environment = lexical_environment;
// Non-standard
eval_context.is_strict_mode = strict_eval;
eval_context->is_strict_mode = strict_eval;
// 15. Push evalContext onto the execution context stack; evalContext is now the running execution context.
TRY(vm.push_execution_context(eval_context, {}));
TRY(vm.push_execution_context(*eval_context, {}));
// 16. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, null, strictEval)).
auto eval_result = eval_declaration_instantiation(vm, program, variable_environment, lexical_environment, nullptr, strict_eval);

View file

@ -22,17 +22,17 @@ public:
[[nodiscard]] Realm const& shadow_realm() const { return m_shadow_realm; }
[[nodiscard]] Realm& shadow_realm() { return m_shadow_realm; }
[[nodiscard]] ExecutionContext const& execution_context() const { return m_execution_context; }
[[nodiscard]] ExecutionContext& execution_context() { return m_execution_context; }
[[nodiscard]] ExecutionContext const& execution_context() const { return *m_execution_context; }
[[nodiscard]] ExecutionContext& execution_context() { return *m_execution_context; }
private:
ShadowRealm(Realm&, ExecutionContext, Object& prototype);
ShadowRealm(Realm&, NonnullOwnPtr<ExecutionContext>, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 3.5 Properties of ShadowRealm Instances, https://tc39.es/proposal-shadowrealm/#sec-properties-of-shadowrealm-instances
NonnullGCPtr<Realm> m_shadow_realm; // [[ShadowRealm]]
ExecutionContext m_execution_context; // [[ExecutionContext]]
NonnullGCPtr<Realm> m_shadow_realm; // [[ShadowRealm]]
NonnullOwnPtr<ExecutionContext> m_execution_context; // [[ExecutionContext]]
};
ThrowCompletionOr<void> copy_name_and_length(VM&, FunctionObject& function, FunctionObject& target, Optional<StringView> prefix = {}, Optional<unsigned> arg_count = {});

View file

@ -47,13 +47,13 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ShadowRealmConstructor::construct(Functi
auto realm = MUST_OR_THROW_OOM(Realm::create(vm));
// 5. Let context be a new execution context.
auto context = ExecutionContext { vm.heap() };
auto context = ExecutionContext::create(vm.heap());
// 6. Set the Function of context to null.
context.function = nullptr;
context->function = nullptr;
// 7. Set the Realm of context to realmRec.
context.realm = realm;
context->realm = realm;
// 8. Set the ScriptOrModule of context to null.
// Note: This is already the default value.

View file

@ -199,31 +199,6 @@ void VM::gather_roots(HashMap<Cell*, HeapRoot>& roots)
for (auto string : m_single_ascii_character_strings)
roots.set(string, HeapRoot { .type = HeapRoot::Type::VM });
auto gather_roots_from_execution_context_stack = [&roots](Vector<ExecutionContext*> const& stack) {
for (auto& execution_context : stack) {
if (execution_context->this_value.is_cell())
roots.set(&execution_context->this_value.as_cell(), { .type = HeapRoot::Type::VM });
for (auto& argument : execution_context->arguments) {
if (argument.is_cell())
roots.set(&argument.as_cell(), HeapRoot { .type = HeapRoot::Type::VM });
}
roots.set(execution_context->lexical_environment, HeapRoot { .type = HeapRoot::Type::VM });
roots.set(execution_context->variable_environment, HeapRoot { .type = HeapRoot::Type::VM });
roots.set(execution_context->private_environment, HeapRoot { .type = HeapRoot::Type::VM });
if (auto context_owner = execution_context->context_owner)
roots.set(context_owner, HeapRoot { .type = HeapRoot::Type::VM });
execution_context->script_or_module.visit(
[](Empty) {},
[&](auto& script_or_module) {
roots.set(script_or_module.ptr(), HeapRoot { .type = HeapRoot::Type::VM });
});
}
};
gather_roots_from_execution_context_stack(m_execution_context_stack);
for (auto& saved_stack : m_saved_execution_context_stacks)
gather_roots_from_execution_context_stack(saved_stack);
#define __JS_ENUMERATE(SymbolName, snake_name) \
roots.set(m_well_known_symbols.snake_name, HeapRoot { .type = HeapRoot::Type::VM });
JS_ENUMERATE_WELL_KNOWN_SYMBOLS

View file

@ -150,8 +150,7 @@ public:
{
if (m_execution_context_stack.is_empty())
return {};
auto& arguments = running_execution_context().arguments;
return index < arguments.size() ? arguments[index] : js_undefined();
return running_execution_context().argument(index);
}
Value this_value() const

View file

@ -343,6 +343,12 @@ public:
return *extract_pointer<Cell>();
}
Cell& as_cell() const
{
VERIFY(is_cell());
return *extract_pointer<Cell>();
}
Accessor& as_accessor()
{
VERIFY(is_accessor());

View file

@ -62,11 +62,11 @@ ThrowCompletionOr<Value> WrappedFunction::internal_call(Value this_argument, Rea
// NOTE: No-op, kept by the VM in its execution context stack.
// 2. Let calleeContext be PrepareForWrappedFunctionCall(F).
ExecutionContext callee_context { vm.heap() };
prepare_for_wrapped_function_call(*this, callee_context);
auto callee_context = ExecutionContext::create(vm.heap());
prepare_for_wrapped_function_call(*this, *callee_context);
// 3. Assert: calleeContext is now the running execution context.
VERIFY(&vm.running_execution_context() == &callee_context);
VERIFY(&vm.running_execution_context() == callee_context);
// 4. Let result be OrdinaryWrappedFunctionCall(F, thisArgument, argumentsList).
auto result = ordinary_wrapped_function_call(*this, this_argument, arguments_list);

View file

@ -108,7 +108,7 @@ SourceTextModule::SourceTextModule(Realm& realm, StringView filename, Script::Ho
RefPtr<ExportStatement const> default_export)
: CyclicModule(realm, filename, has_top_level_await, move(requested_modules), host_defined)
, m_ecmascript_code(move(body))
, m_execution_context(realm.heap())
, m_execution_context(ExecutionContext::create(realm.heap()))
, m_import_entries(move(import_entries))
, m_local_export_entries(move(local_export_entries))
, m_indirect_export_entries(move(indirect_export_entries))
@ -410,16 +410,16 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
// Note: This must be true because we use a reference.
// 11. Set the Realm of moduleContext to module.[[Realm]].
m_execution_context.realm = &realm();
m_execution_context->realm = &realm();
// 12. Set the ScriptOrModule of moduleContext to module.
m_execution_context.script_or_module = NonnullGCPtr<Module>(*this);
m_execution_context->script_or_module = NonnullGCPtr<Module>(*this);
// 13. Set the VariableEnvironment of moduleContext to module.[[Environment]].
m_execution_context.variable_environment = environment;
m_execution_context->variable_environment = environment;
// 14. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
m_execution_context.lexical_environment = environment;
m_execution_context->lexical_environment = environment;
// 15. Set the PrivateEnvironment of moduleContext to null.
@ -427,7 +427,7 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
// Note: We're already working on that one.
// 17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
TRY(vm.push_execution_context(m_execution_context, {}));
TRY(vm.push_execution_context(*m_execution_context, {}));
// 18. Let code be module.[[ECMAScriptCode]].
@ -662,27 +662,27 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] SourceTextModule::execute_module({}, PromiseCapability @ {})", filename(), capability.ptr());
// 1. Let moduleContext be a new ECMAScript code execution context.
ExecutionContext module_context { vm.heap() };
auto module_context = ExecutionContext::create(vm.heap());
// Note: This is not in the spec but we require it.
module_context.is_strict_mode = true;
module_context->is_strict_mode = true;
// 2. Set the Function of moduleContext to null.
// 3. Set the Realm of moduleContext to module.[[Realm]].
module_context.realm = &realm();
module_context->realm = &realm();
// 4. Set the ScriptOrModule of moduleContext to module.
module_context.script_or_module = NonnullGCPtr<Module>(*this);
module_context->script_or_module = NonnullGCPtr<Module>(*this);
// 5. Assert: module has been linked and declarations in its module environment have been instantiated.
VERIFY(m_status != ModuleStatus::Unlinked && m_status != ModuleStatus::Linking && environment());
// 6. Set the VariableEnvironment of moduleContext to module.[[Environment]].
module_context.variable_environment = environment();
module_context->variable_environment = environment();
// 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
module_context.lexical_environment = environment();
module_context->lexical_environment = environment();
// 8. Suspend the currently running execution context.
// FIXME: We don't have suspend yet
@ -692,7 +692,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
// a. Assert: capability is not present.
VERIFY(capability == nullptr);
// b. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
TRY(vm.push_execution_context(module_context, {}));
TRY(vm.push_execution_context(*module_context, {}));
// c. Let result be the result of evaluating module.[[ECMAScriptCode]].
Completion result;
@ -713,7 +713,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
}
// d. Let env be moduleContext's LexicalEnvironment.
auto env = module_context.lexical_environment;
auto env = module_context->lexical_environment;
VERIFY(is<DeclarativeEnvironment>(*env));
// e. Set result to DisposeResources(env, result).
@ -737,7 +737,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
VERIFY(capability != nullptr);
// b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).
async_block_start<NonnullRefPtr<Statement const>>(vm, m_ecmascript_code, *capability, module_context);
async_block_start<NonnullRefPtr<Statement const>>(vm, m_ecmascript_code, *capability, *module_context);
}
// 11. Return unused.

View file

@ -41,13 +41,13 @@ private:
virtual void visit_edges(Cell::Visitor&) override;
NonnullRefPtr<Program> m_ecmascript_code; // [[ECMAScriptCode]]
ExecutionContext m_execution_context; // [[Context]]
GCPtr<Object> m_import_meta; // [[ImportMeta]]
Vector<ImportEntry> m_import_entries; // [[ImportEntries]]
Vector<ExportEntry> m_local_export_entries; // [[LocalExportEntries]]
Vector<ExportEntry> m_indirect_export_entries; // [[IndirectExportEntries]]
Vector<ExportEntry> m_star_export_entries; // [[StarExportEntries]]
NonnullRefPtr<Program> m_ecmascript_code; // [[ECMAScriptCode]]
NonnullOwnPtr<ExecutionContext> m_execution_context; // [[Context]]
GCPtr<Object> m_import_meta; // [[ImportMeta]]
Vector<ImportEntry> m_import_entries; // [[ImportEntries]]
Vector<ExportEntry> m_local_export_entries; // [[LocalExportEntries]]
Vector<ExportEntry> m_indirect_export_entries; // [[IndirectExportEntries]]
Vector<ExportEntry> m_star_export_entries; // [[StarExportEntries]]
RefPtr<ExportStatement const> m_default_export; // Note: Not from the spec
};

View file

@ -79,25 +79,25 @@ ThrowCompletionOr<Promise*> SyntheticModule::evaluate(VM& vm)
// FIXME: We don't have suspend yet.
// 2. Let moduleContext be a new ECMAScript code execution context.
ExecutionContext module_context { vm.heap() };
auto module_context = ExecutionContext::create(vm.heap());
// 3. Set the Function of moduleContext to null.
// Note: This is the default value.
// 4. Set the Realm of moduleContext to module.[[Realm]].
module_context.realm = &realm();
module_context->realm = &realm();
// 5. Set the ScriptOrModule of moduleContext to module.
module_context.script_or_module = NonnullGCPtr<Module>(*this);
module_context->script_or_module = NonnullGCPtr<Module>(*this);
// 6. Set the VariableEnvironment of moduleContext to module.[[Environment]].
module_context.variable_environment = environment();
module_context->variable_environment = environment();
// 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
module_context.lexical_environment = environment();
module_context->lexical_environment = environment();
// 8. Push moduleContext on to the execution context stack; moduleContext is now the running execution context.
TRY(vm.push_execution_context(module_context, {}));
TRY(vm.push_execution_context(*module_context, {}));
// 9. Let result be the result of performing module.[[EvaluationSteps]](module).
auto result = m_evaluation_steps(*this);

View file

@ -189,7 +189,7 @@ ErrorOr<void> initialize_main_thread_vm()
};
// 8.1.5.4.1 HostCallJobCallback(callback, V, argumentsList), https://html.spec.whatwg.org/multipage/webappapis.html#hostcalljobcallback
s_main_thread_vm->host_call_job_callback = [](JS::JobCallback& callback, JS::Value this_value, JS::MarkedVector<JS::Value> arguments_list) {
s_main_thread_vm->host_call_job_callback = [](JS::JobCallback& callback, JS::Value this_value, ReadonlySpan<JS::Value> arguments_list) {
auto& callback_host_defined = verify_cast<WebEngineCustomJobCallbackData>(*callback.custom_data);
// 1. Let incumbent settings be callback.[[HostDefined]].[[IncumbentSettings]]. (NOTE: Not necessary)
@ -203,7 +203,7 @@ ErrorOr<void> initialize_main_thread_vm()
s_main_thread_vm->push_execution_context(*callback_host_defined.active_script_context);
// 5. Let result be Call(callback.[[Callback]], V, argumentsList).
auto result = JS::call(*s_main_thread_vm, *callback.callback.cell(), this_value, move(arguments_list));
auto result = JS::call(*s_main_thread_vm, *callback.callback.cell(), this_value, arguments_list);
// 6. If script execution context is not null, then pop script execution context from the JavaScript execution context stack.
if (callback_host_defined.active_script_context) {
@ -267,7 +267,7 @@ ErrorOr<void> initialize_main_thread_vm()
// NOTE: This keeps job_settings alive by keeping realm alive, which is holding onto job_settings.
HTML::queue_a_microtask(script ? script->settings_object().responsible_document().ptr() : nullptr, [job_settings, job = move(job), script_or_module = move(script_or_module)] {
// The dummy execution context has to be kept up here to keep it alive for the duration of the function.
Optional<JS::ExecutionContext> dummy_execution_context;
OwnPtr<JS::ExecutionContext> dummy_execution_context;
if (job_settings) {
// 1. If job settings is not null, then check if we can run script with job settings. If this returns "do not run" then return.
@ -289,9 +289,9 @@ ErrorOr<void> initialize_main_thread_vm()
// FIXME: We need to setup a dummy execution context in case a JS::NativeFunction is called when processing the job.
// This is because JS::NativeFunction::call excepts something to be on the execution context stack to be able to get the caller context to initialize the environment.
// Do note that the JS spec gives _no_ guarantee that the execution context stack has something on it if HostEnqueuePromiseJob was called with a null realm: https://tc39.es/ecma262/#job-preparedtoevaluatecode
dummy_execution_context = JS::ExecutionContext { s_main_thread_vm->heap() };
dummy_execution_context = JS::ExecutionContext::create(s_main_thread_vm->heap());
dummy_execution_context->script_or_module = script_or_module;
s_main_thread_vm->push_execution_context(dummy_execution_context.value());
s_main_thread_vm->push_execution_context(*dummy_execution_context);
}
// 3. Let result be job().
@ -331,7 +331,7 @@ ErrorOr<void> initialize_main_thread_vm()
// 4. If active script is not null, set script execution context to a new JavaScript execution context, with its Function field set to null,
// its Realm field set to active script's settings object's Realm, and its ScriptOrModule set to active script's record.
if (script) {
script_execution_context = adopt_own(*new JS::ExecutionContext(s_main_thread_vm->heap()));
script_execution_context = JS::ExecutionContext::create(s_main_thread_vm->heap());
script_execution_context->function = nullptr;
script_execution_context->realm = &script->settings_object().realm();
if (is<HTML::ClassicScript>(script)) {

View file

@ -193,7 +193,7 @@ JS::ThrowCompletionOr<size_t> instantiate_module(JS::VM& vm, Wasm::Module const&
for (auto& entry : arguments)
argument_values.append(to_js_value(vm, entry));
auto result = TRY(JS::call(vm, function, JS::js_undefined(), move(argument_values)));
auto result = TRY(JS::call(vm, function, JS::js_undefined(), argument_values.span()));
if (type.results().is_empty())
return Wasm::Result { Vector<Wasm::Value> {} };

View file

@ -175,7 +175,7 @@ JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, Deprec
// 12. Let callResult be Call(X, thisArg, esArgs).
VERIFY(actual_function_object);
auto& vm = object->vm();
auto call_result = JS::call(vm, verify_cast<JS::FunctionObject>(*actual_function_object), this_argument.value(), move(args));
auto call_result = JS::call(vm, verify_cast<JS::FunctionObject>(*actual_function_object), this_argument.value(), args.span());
// 13. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) {
@ -233,7 +233,7 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
// 11. Let callResult be Call(F, thisArg, esArgs).
auto& vm = function_object->vm();
auto call_result = JS::call(vm, verify_cast<JS::FunctionObject>(*function_object), this_argument.value(), move(args));
auto call_result = JS::call(vm, verify_cast<JS::FunctionObject>(*function_object), this_argument.value(), args.span());
// 12. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) {
@ -280,7 +280,7 @@ JS::Completion construct(WebIDL::CallbackType& callback, JS::MarkedVector<JS::Va
// 10. Let callResult be Completion(Construct(F, esArgs)).
auto& vm = function_object->vm();
auto call_result = JS::construct(vm, verify_cast<JS::FunctionObject>(*function_object), move(args));
auto call_result = JS::construct(vm, verify_cast<JS::FunctionObject>(*function_object), args.span());
// 11. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) {