1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 18:07:34 +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

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