mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibGC+Everywhere: Factor out a LibGC from LibJS
Resulting in a massive rename across almost everywhere! Alongside the namespace change, we now have the following names: * JS::NonnullGCPtr -> GC::Ref * JS::GCPtr -> GC::Ptr * JS::HeapFunction -> GC::Function * JS::CellImpl -> GC::Cell * JS::Handle -> GC::Root
This commit is contained in:
parent
ce23efc5f6
commit
f87041bf3a
Notes:
github-actions[bot]
2024-11-15 13:50:17 +00:00
Author: https://github.com/shannonbooth
Commit: f87041bf3a
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2345
1722 changed files with 9939 additions and 9906 deletions
|
@ -14,9 +14,9 @@
|
|||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibGC/ConservativeVector.h>
|
||||
#include <LibGC/MarkedVector.h>
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Heap/ConservativeVector.h>
|
||||
#include <LibJS/Heap/MarkedVector.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Accessor.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
|
@ -97,7 +97,7 @@ Value FunctionExpression::instantiate_ordinary_function_expression(VM& vm, Depre
|
|||
auto has_own_name = !name().is_empty();
|
||||
|
||||
auto const used_name = has_own_name ? name() : given_name.view();
|
||||
auto environment = NonnullGCPtr { *vm.running_execution_context().lexical_environment };
|
||||
auto environment = GC::Ref { *vm.running_execution_context().lexical_environment };
|
||||
if (has_own_name) {
|
||||
VERIFY(environment);
|
||||
environment = new_declarative_environment(*environment);
|
||||
|
@ -227,7 +227,7 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation
|
|||
auto& realm = *vm.current_realm();
|
||||
|
||||
auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key));
|
||||
GCPtr<ECMAScriptFunctionObject> initializer;
|
||||
GC::Ptr<ECMAScriptFunctionObject> initializer;
|
||||
if (m_initializer) {
|
||||
auto copy_initializer = m_initializer;
|
||||
auto name = property_key_or_private_name.visit(
|
||||
|
@ -310,7 +310,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
|
|||
|
||||
vm.running_execution_context().lexical_environment = class_environment;
|
||||
|
||||
auto proto_parent = GCPtr { realm.intrinsics().object_prototype() };
|
||||
auto proto_parent = GC::Ptr { realm.intrinsics().object_prototype() };
|
||||
auto constructor_parent = realm.intrinsics().function_prototype();
|
||||
|
||||
if (!m_super_class.is_null()) {
|
||||
|
@ -366,12 +366,12 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
|
|||
|
||||
prototype->define_direct_property(vm.names.constructor, class_constructor, Attribute::Writable | Attribute::Configurable);
|
||||
|
||||
using StaticElement = Variant<ClassFieldDefinition, JS::NonnullGCPtr<ECMAScriptFunctionObject>>;
|
||||
using StaticElement = Variant<ClassFieldDefinition, GC::Ref<ECMAScriptFunctionObject>>;
|
||||
|
||||
ConservativeVector<PrivateElement> static_private_methods(vm.heap());
|
||||
ConservativeVector<PrivateElement> instance_private_methods(vm.heap());
|
||||
ConservativeVector<ClassFieldDefinition> instance_fields(vm.heap());
|
||||
ConservativeVector<StaticElement> static_elements(vm.heap());
|
||||
GC::ConservativeVector<PrivateElement> static_private_methods(vm.heap());
|
||||
GC::ConservativeVector<PrivateElement> instance_private_methods(vm.heap());
|
||||
GC::ConservativeVector<ClassFieldDefinition> instance_fields(vm.heap());
|
||||
GC::ConservativeVector<StaticElement> static_elements(vm.heap());
|
||||
|
||||
for (size_t element_index = 0; element_index < m_elements.size(); element_index++) {
|
||||
auto const& element = m_elements[element_index];
|
||||
|
@ -411,7 +411,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
|
|||
VERIFY(element_value.has<Completion>() && element_value.get<Completion>().value().has_value());
|
||||
auto& element_object = element_value.get<Completion>().value()->as_object();
|
||||
VERIFY(is<ECMAScriptFunctionObject>(element_object));
|
||||
static_elements.append(NonnullGCPtr { static_cast<ECMAScriptFunctionObject&>(element_object) });
|
||||
static_elements.append(GC::Ref { static_cast<ECMAScriptFunctionObject&>(element_object) });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,7 +435,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
|
|||
[&](ClassFieldDefinition& field) -> ThrowCompletionOr<void> {
|
||||
return TRY(class_constructor->define_field(field));
|
||||
},
|
||||
[&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
|
||||
[&](GC::Root<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
|
||||
VERIFY(!static_block_function.is_null());
|
||||
// We discard any value returned here.
|
||||
TRY(call(vm, *static_block_function.cell(), class_constructor));
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <AK/RefPtr.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGC/Root.h>
|
||||
#include <LibJS/Bytecode/CodeGenerationError.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||
|
@ -22,7 +23,6 @@
|
|||
#include <LibJS/Bytecode/Operand.h>
|
||||
#include <LibJS/Bytecode/ScopedOperand.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibJS/Runtime/ClassFieldDefinition.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/EnvironmentCoordinate.h>
|
||||
|
@ -160,10 +160,10 @@ public:
|
|||
}
|
||||
|
||||
Bytecode::Executable* bytecode_executable() const { return m_bytecode_executable; }
|
||||
void set_bytecode_executable(Bytecode::Executable* bytecode_executable) { m_bytecode_executable = make_handle(bytecode_executable); }
|
||||
void set_bytecode_executable(Bytecode::Executable* bytecode_executable) { m_bytecode_executable = make_root(bytecode_executable); }
|
||||
|
||||
private:
|
||||
Handle<Bytecode::Executable> m_bytecode_executable;
|
||||
GC::Root<Bytecode::Executable> m_bytecode_executable;
|
||||
};
|
||||
|
||||
// 14.13 Labelled Statements, https://tc39.es/ecma262/#sec-labelled-statements
|
||||
|
@ -690,7 +690,7 @@ struct FunctionParameter {
|
|||
Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>> binding;
|
||||
RefPtr<Expression const> default_value;
|
||||
bool is_rest { false };
|
||||
Handle<Bytecode::Executable> bytecode_executable {};
|
||||
GC::Root<Bytecode::Executable> bytecode_executable {};
|
||||
};
|
||||
|
||||
struct FunctionParsingInsights {
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibGC/Root.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/ScopedOperand.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
struct UnwindInfo {
|
||||
JS::GCPtr<Executable const> executable;
|
||||
JS::GCPtr<Environment> lexical_environment;
|
||||
GC::Ptr<Executable const> executable;
|
||||
GC::Ptr<Environment> lexical_environment;
|
||||
|
||||
bool handler_called { false };
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(Executable);
|
||||
GC_DEFINE_ALLOCATOR(Executable);
|
||||
|
||||
Executable::Executable(
|
||||
Vector<u8> bytecode,
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibGC/CellAllocator.h>
|
||||
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||
#include <LibJS/Bytecode/Label.h>
|
||||
#include <LibJS/Bytecode/StringTable.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibJS/Heap/CellAllocator.h>
|
||||
#include <LibJS/Runtime/EnvironmentCoordinate.h>
|
||||
#include <LibJS/SourceRange.h>
|
||||
|
||||
|
@ -40,8 +40,8 @@ struct SourceRecord {
|
|||
};
|
||||
|
||||
class Executable final : public Cell {
|
||||
JS_CELL(Executable, Cell);
|
||||
JS_DECLARE_ALLOCATOR(Executable);
|
||||
GC_CELL(Executable, Cell);
|
||||
GC_DECLARE_ALLOCATOR(Executable);
|
||||
|
||||
public:
|
||||
Executable(
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
Generator::Generator(VM& vm, GCPtr<ECMAScriptFunctionObject const> function, MustPropagateCompletion must_propagate_completion)
|
||||
Generator::Generator(VM& vm, GC::Ptr<ECMAScriptFunctionObject const> function, MustPropagateCompletion must_propagate_completion)
|
||||
: m_vm(vm)
|
||||
, m_string_table(make<StringTable>())
|
||||
, m_identifier_table(make<IdentifierTable>())
|
||||
|
@ -199,7 +199,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
|
|||
return {};
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GCPtr<ECMAScriptFunctionObject const> function, MustPropagateCompletion must_propagate_completion, Vector<DeprecatedFlyString> local_variable_names)
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr<ECMAScriptFunctionObject const> function, MustPropagateCompletion must_propagate_completion, Vector<DeprecatedFlyString> local_variable_names)
|
||||
{
|
||||
Generator generator(vm, function, must_propagate_completion);
|
||||
|
||||
|
@ -460,7 +460,7 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::compile(VM& vm, ASTNo
|
|||
return executable;
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate_from_ast_node(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind)
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::generate_from_ast_node(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind)
|
||||
{
|
||||
Vector<DeprecatedFlyString> local_variable_names;
|
||||
if (is<ScopeNode>(node))
|
||||
|
@ -468,7 +468,7 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate_from_ast_nod
|
|||
return compile(vm, node, enclosing_function_kind, {}, MustPropagateCompletion::Yes, move(local_variable_names));
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate_from_function(VM& vm, ECMAScriptFunctionObject const& function)
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::generate_from_function(VM& vm, ECMAScriptFunctionObject const& function)
|
||||
{
|
||||
return compile(vm, function.ecmascript_code(), function.kind(), &function, MustPropagateCompletion::No, function.local_variables_names());
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ public:
|
|||
Yes,
|
||||
};
|
||||
|
||||
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal);
|
||||
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate_from_function(VM&, ECMAScriptFunctionObject const& function);
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal);
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> generate_from_function(VM&, ECMAScriptFunctionObject const& function);
|
||||
|
||||
CodeGenerationErrorOr<void> emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function);
|
||||
|
||||
|
@ -343,7 +343,7 @@ public:
|
|||
private:
|
||||
VM& m_vm;
|
||||
|
||||
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> compile(VM&, ASTNode const&, FunctionKind, GCPtr<ECMAScriptFunctionObject const>, MustPropagateCompletion, Vector<DeprecatedFlyString> local_variable_names);
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> compile(VM&, ASTNode const&, FunctionKind, GC::Ptr<ECMAScriptFunctionObject const>, MustPropagateCompletion, Vector<DeprecatedFlyString> local_variable_names);
|
||||
|
||||
enum class JumpType {
|
||||
Continue,
|
||||
|
@ -352,7 +352,7 @@ private:
|
|||
void generate_scoped_jump(JumpType);
|
||||
void generate_labelled_jump(JumpType, DeprecatedFlyString const& label);
|
||||
|
||||
Generator(VM&, GCPtr<ECMAScriptFunctionObject const>, MustPropagateCompletion);
|
||||
Generator(VM&, GC::Ptr<ECMAScriptFunctionObject const>, MustPropagateCompletion);
|
||||
~Generator() = default;
|
||||
|
||||
void grow(size_t);
|
||||
|
@ -373,7 +373,7 @@ private:
|
|||
NonnullOwnPtr<StringTable> m_string_table;
|
||||
NonnullOwnPtr<IdentifierTable> m_identifier_table;
|
||||
NonnullOwnPtr<RegexTable> m_regex_table;
|
||||
MarkedVector<Value> m_constants;
|
||||
GC::MarkedVector<Value> m_constants;
|
||||
|
||||
mutable Optional<ScopedOperand> m_true_constant;
|
||||
mutable Optional<ScopedOperand> m_false_constant;
|
||||
|
@ -401,7 +401,7 @@ private:
|
|||
bool m_finished { false };
|
||||
bool m_must_propagate_completion { true };
|
||||
|
||||
GCPtr<ECMAScriptFunctionObject const> m_function;
|
||||
GC::Ptr<ECMAScriptFunctionObject const> m_function;
|
||||
|
||||
Optional<IdentifierTableIndex> m_length_identifier;
|
||||
};
|
||||
|
|
|
@ -213,7 +213,7 @@ private:
|
|||
u8 const* m_begin { nullptr };
|
||||
u8 const* m_end { nullptr };
|
||||
u8 const* m_ptr { nullptr };
|
||||
GCPtr<Executable const> m_executable;
|
||||
GC::Ptr<Executable const> m_executable;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ ALWAYS_INLINE Value Interpreter::do_yield(Value value, Optional<Label> continuat
|
|||
}
|
||||
|
||||
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
|
||||
ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Environment> lexical_environment_override)
|
||||
ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environment> lexical_environment_override)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
@ -199,7 +199,7 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Envir
|
|||
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 = GC::Ref<Script>(script_record);
|
||||
|
||||
// 6. Set the VariableEnvironment of scriptContext to globalEnv.
|
||||
script_context->variable_environment = &global_environment;
|
||||
|
@ -703,11 +703,11 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
|||
{
|
||||
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
|
||||
|
||||
TemporaryChange restore_executable { m_current_executable, GCPtr { executable } };
|
||||
TemporaryChange restore_executable { m_current_executable, GC::Ptr { executable } };
|
||||
TemporaryChange restore_saved_jump { m_scheduled_jump, Optional<size_t> {} };
|
||||
TemporaryChange restore_realm { m_realm, GCPtr { vm().current_realm() } };
|
||||
TemporaryChange restore_global_object { m_global_object, GCPtr { m_realm->global_object() } };
|
||||
TemporaryChange restore_global_declarative_environment { m_global_declarative_environment, GCPtr { m_realm->global_environment().declarative_record() } };
|
||||
TemporaryChange restore_realm { m_realm, GC::Ptr { vm().current_realm() } };
|
||||
TemporaryChange restore_global_object { m_global_object, GC::Ptr { m_realm->global_object() } };
|
||||
TemporaryChange restore_global_declarative_environment { m_global_declarative_environment, GC::Ptr { m_realm->global_environment().declarative_record() } };
|
||||
|
||||
VERIFY(!vm().execution_context_stack().is_empty());
|
||||
|
||||
|
@ -813,7 +813,7 @@ void Interpreter::enter_object_environment(Object& object)
|
|||
running_execution_context().lexical_environment = new_object_environment(object, true, old_environment);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
|
||||
{
|
||||
auto executable_result = Bytecode::Generator::generate_from_ast_node(vm, node, kind);
|
||||
if (executable_result.is_error())
|
||||
|
@ -828,7 +828,7 @@ ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM& vm, ASTNode co
|
|||
return bytecode_executable;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM& vm, ECMAScriptFunctionObject const& function)
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM& vm, ECMAScriptFunctionObject const& function)
|
||||
{
|
||||
auto const& name = function.name();
|
||||
|
||||
|
@ -921,7 +921,7 @@ ALWAYS_INLINE Completion throw_null_or_undefined_property_access(VM& vm, Value b
|
|||
return vm.throw_completion<TypeError>(ErrorType::ToObjectNullOrUndefined);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE GCPtr<Object> base_object_for_get_impl(VM& vm, Value base_value)
|
||||
ALWAYS_INLINE GC::Ptr<Object> base_object_for_get_impl(VM& vm, Value base_value)
|
||||
{
|
||||
if (base_value.is_object()) [[likely]]
|
||||
return base_value.as_object();
|
||||
|
@ -942,19 +942,19 @@ ALWAYS_INLINE GCPtr<Object> base_object_for_get_impl(VM& vm, Value base_value)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThrowCompletionOr<NonnullGCPtr<Object>> base_object_for_get(VM& vm, Value base_value, Optional<IdentifierTableIndex> base_identifier, IdentifierTableIndex property_identifier, Executable const& executable)
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> base_object_for_get(VM& vm, Value base_value, Optional<IdentifierTableIndex> base_identifier, IdentifierTableIndex property_identifier, Executable const& executable)
|
||||
{
|
||||
if (auto base_object = base_object_for_get_impl(vm, base_value))
|
||||
return NonnullGCPtr { *base_object };
|
||||
return GC::Ref { *base_object };
|
||||
|
||||
// NOTE: At this point this is guaranteed to throw (null or undefined).
|
||||
return throw_null_or_undefined_property_get(vm, base_value, base_identifier, property_identifier, executable);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThrowCompletionOr<NonnullGCPtr<Object>> base_object_for_get(VM& vm, Value base_value, Optional<IdentifierTableIndex> base_identifier, Value property, Executable const& executable)
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> base_object_for_get(VM& vm, Value base_value, Optional<IdentifierTableIndex> base_identifier, Value property, Executable const& executable)
|
||||
{
|
||||
if (auto base_object = base_object_for_get_impl(vm, base_value))
|
||||
return NonnullGCPtr { *base_object };
|
||||
return GC::Ref { *base_object };
|
||||
|
||||
// NOTE: At this point this is guaranteed to throw (null or undefined).
|
||||
return throw_null_or_undefined_property_get(vm, base_value, base_identifier, property, executable);
|
||||
|
@ -1131,10 +1131,10 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
|
|||
|
||||
auto& identifier = interpreter.current_executable().get_identifier(identifier_index);
|
||||
|
||||
if (vm.running_execution_context().script_or_module.has<NonnullGCPtr<Module>>()) {
|
||||
if (vm.running_execution_context().script_or_module.has<GC::Ref<Module>>()) {
|
||||
// NOTE: GetGlobal is used to access variables stored in the module environment and global environment.
|
||||
// The module environment is checked first since it precedes the global environment in the environment chain.
|
||||
auto& module_environment = *vm.running_execution_context().script_or_module.get<NonnullGCPtr<Module>>()->environment();
|
||||
auto& module_environment = *vm.running_execution_context().script_or_module.get<GC::Ref<Module>>()->environment();
|
||||
if (TRY(module_environment.has_binding(identifier))) {
|
||||
// TODO: Cache offset of binding value
|
||||
return TRY(module_environment.get_binding_value(vm, identifier, vm.in_strict_mode()));
|
||||
|
@ -1436,13 +1436,13 @@ inline Value new_regexp(VM& vm, ParsedRegex const& parsed_regex, ByteString cons
|
|||
}
|
||||
|
||||
// 13.3.8.1 https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
|
||||
inline MarkedVector<Value> argument_list_evaluation(VM& vm, Value arguments)
|
||||
inline GC::MarkedVector<Value> argument_list_evaluation(VM& vm, Value arguments)
|
||||
{
|
||||
// Note: Any spreading and actual evaluation is handled in preceding opcodes
|
||||
// Note: The spec uses the concept of a list, while we create a temporary array
|
||||
// in the preceding opcodes, so we have to convert in a manner that is not
|
||||
// visible to the user
|
||||
MarkedVector<Value> argument_values { vm.heap() };
|
||||
GC::MarkedVector<Value> argument_values { vm.heap() };
|
||||
|
||||
auto& argument_array = arguments.as_array();
|
||||
auto array_length = argument_array.indexed_properties().array_like_size();
|
||||
|
@ -1507,7 +1507,7 @@ inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value supe
|
|||
}
|
||||
|
||||
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
|
||||
inline ThrowCompletionOr<NonnullGCPtr<Object>> super_call_with_argument_array(VM& vm, Value argument_array, bool is_synthetic)
|
||||
inline ThrowCompletionOr<GC::Ref<Object>> super_call_with_argument_array(VM& vm, Value argument_array, bool is_synthetic)
|
||||
{
|
||||
// 1. Let newTarget be GetNewTarget().
|
||||
auto new_target = vm.get_new_target();
|
||||
|
@ -1519,7 +1519,7 @@ inline ThrowCompletionOr<NonnullGCPtr<Object>> super_call_with_argument_array(VM
|
|||
auto* func = get_super_constructor(vm);
|
||||
|
||||
// 4. Let argList be ? ArgumentListEvaluation of Arguments.
|
||||
MarkedVector<Value> arg_list { vm.heap() };
|
||||
GC::MarkedVector<Value> arg_list { vm.heap() };
|
||||
if (is_synthetic) {
|
||||
VERIFY(argument_array.is_object() && is<Array>(argument_array.as_object()));
|
||||
auto const& array_value = static_cast<Array const&>(argument_array.as_object());
|
||||
|
@ -1556,7 +1556,7 @@ inline ThrowCompletionOr<NonnullGCPtr<Object>> super_call_with_argument_array(VM
|
|||
return result;
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<NonnullGCPtr<Array>> iterator_to_array(VM& vm, Value iterator)
|
||||
inline ThrowCompletionOr<GC::Ref<Array>> iterator_to_array(VM& vm, Value iterator)
|
||||
{
|
||||
auto& iterator_record = verify_cast<IteratorRecord>(iterator.as_object());
|
||||
|
||||
|
@ -1684,9 +1684,9 @@ inline ThrowCompletionOr<Object*> get_object_property_iterator(VM& vm, Value val
|
|||
// so we just keep the order consistent anyway.
|
||||
OrderedHashTable<PropertyKey> properties;
|
||||
OrderedHashTable<PropertyKey> non_enumerable_properties;
|
||||
HashTable<NonnullGCPtr<Object>> seen_objects;
|
||||
HashTable<GC::Ref<Object>> seen_objects;
|
||||
// Collect all keys immediately (invariant no. 5)
|
||||
for (auto object_to_check = GCPtr { object.ptr() }; object_to_check && !seen_objects.contains(*object_to_check); object_to_check = TRY(object_to_check->internal_get_prototype_of())) {
|
||||
for (auto object_to_check = GC::Ptr { object.ptr() }; object_to_check && !seen_objects.contains(*object_to_check); object_to_check = TRY(object_to_check->internal_get_prototype_of())) {
|
||||
seen_objects.set(*object_to_check);
|
||||
for (auto& key : TRY(object_to_check->internal_own_property_keys())) {
|
||||
if (key.is_symbol())
|
||||
|
@ -2207,7 +2207,7 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter)
|
|||
auto make_and_swap_envs = [&](auto& old_environment) {
|
||||
auto declarative_environment = new_declarative_environment(*old_environment).ptr();
|
||||
declarative_environment->ensure_capacity(m_capacity);
|
||||
GCPtr<Environment> environment = declarative_environment;
|
||||
GC::Ptr<Environment> environment = declarative_environment;
|
||||
swap(old_environment, environment);
|
||||
return environment;
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
VM& vm() { return m_vm; }
|
||||
VM const& vm() const { return m_vm; }
|
||||
|
||||
ThrowCompletionOr<Value> run(Script&, JS::GCPtr<Environment> lexical_environment_override = nullptr);
|
||||
ThrowCompletionOr<Value> run(Script&, GC::Ptr<Environment> lexical_environment_override = nullptr);
|
||||
ThrowCompletionOr<Value> run(SourceTextModule&);
|
||||
|
||||
ThrowCompletionOr<Value> run(Bytecode::Executable& executable, Optional<size_t> entry_point = {}, Value initial_accumulator_value = {})
|
||||
|
@ -96,10 +96,10 @@ private:
|
|||
|
||||
VM& m_vm;
|
||||
Optional<size_t> m_scheduled_jump;
|
||||
GCPtr<Executable> m_current_executable { nullptr };
|
||||
GCPtr<Realm> m_realm { nullptr };
|
||||
GCPtr<Object> m_global_object { nullptr };
|
||||
GCPtr<DeclarativeEnvironment> m_global_declarative_environment { nullptr };
|
||||
GC::Ptr<Executable> m_current_executable { nullptr };
|
||||
GC::Ptr<Realm> m_realm { nullptr };
|
||||
GC::Ptr<Object> m_global_object { nullptr };
|
||||
GC::Ptr<DeclarativeEnvironment> m_global_declarative_environment { nullptr };
|
||||
Optional<size_t&> m_program_counter;
|
||||
Span<Value> m_arguments;
|
||||
Span<Value> m_registers_and_constants_and_locals;
|
||||
|
@ -109,7 +109,7 @@ private:
|
|||
|
||||
extern bool g_dump_bytecode;
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM&, ASTNode const&, JS::FunctionKind kind, DeprecatedFlyString const& name);
|
||||
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM&, ECMAScriptFunctionObject const&);
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM&, ASTNode const&, JS::FunctionKind kind, DeprecatedFlyString const& name);
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM&, ECMAScriptFunctionObject const&);
|
||||
|
||||
}
|
||||
|
|
|
@ -19,16 +19,7 @@ set(SOURCES
|
|||
Contrib/Test262/GlobalObject.cpp
|
||||
Contrib/Test262/IsHTMLDDA.cpp
|
||||
CyclicModule.cpp
|
||||
Heap/BlockAllocator.cpp
|
||||
Heap/Cell.cpp
|
||||
Heap/CellImpl.cpp
|
||||
Heap/CellAllocator.cpp
|
||||
Heap/ConservativeVector.cpp
|
||||
Heap/Handle.cpp
|
||||
Heap/Heap.cpp
|
||||
Heap/HeapBlock.cpp
|
||||
Heap/MarkedVector.cpp
|
||||
Heap/WeakContainer.cpp
|
||||
Lexer.cpp
|
||||
MarkupGenerator.cpp
|
||||
Module.cpp
|
||||
|
@ -275,7 +266,7 @@ set(SOURCES
|
|||
)
|
||||
|
||||
serenity_lib(LibJS js)
|
||||
target_link_libraries(LibJS PRIVATE LibCore LibCrypto LibFileSystem LibRegex LibSyntax)
|
||||
target_link_libraries(LibJS PRIVATE LibCore LibCrypto LibFileSystem LibRegex LibSyntax LibGC)
|
||||
|
||||
# Link LibUnicode publicly to ensure ICU data (which is in libicudata.a) is available in any process using LibJS.
|
||||
target_link_libraries(LibJS PUBLIC LibUnicode)
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(Console);
|
||||
JS_DEFINE_ALLOCATOR(ConsoleClient);
|
||||
GC_DEFINE_ALLOCATOR(Console);
|
||||
GC_DEFINE_ALLOCATOR(ConsoleClient);
|
||||
|
||||
Console::Console(Realm& realm)
|
||||
: m_realm(realm)
|
||||
|
@ -51,7 +51,7 @@ ThrowCompletionOr<Value> Console::assert_()
|
|||
auto message = PrimitiveString::create(vm, "Assertion failed"_string);
|
||||
|
||||
// NOTE: Assemble `data` from the function arguments.
|
||||
MarkedVector<Value> data { vm.heap() };
|
||||
GC::MarkedVector<Value> data { vm.heap() };
|
||||
if (vm.argument_count() > 1) {
|
||||
data.ensure_capacity(vm.argument_count() - 1);
|
||||
for (size_t i = 1; i < vm.argument_count(); ++i) {
|
||||
|
@ -143,7 +143,7 @@ ThrowCompletionOr<Value> Console::log()
|
|||
}
|
||||
|
||||
// To [create table row] given tabularDataItem, rowIndex, list finalColumns, and optional list properties, perform the following steps:
|
||||
static ThrowCompletionOr<NonnullGCPtr<Object>> create_table_row(Realm& realm, Value row_index, Value tabular_data_item, MarkedVector<Value>& final_columns, HashMap<PropertyKey, bool>& visited_columns, HashMap<PropertyKey, bool>& properties)
|
||||
static ThrowCompletionOr<GC::Ref<Object>> create_table_row(Realm& realm, Value row_index, Value tabular_data_item, GC::MarkedVector<Value>& final_columns, HashMap<PropertyKey, bool>& visited_columns, HashMap<PropertyKey, bool>& properties)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
|
@ -265,10 +265,10 @@ ThrowCompletionOr<Value> Console::table()
|
|||
}
|
||||
|
||||
// 1. Let `finalRows` be the new list, initially empty
|
||||
MarkedVector<Value> final_rows(vm.heap());
|
||||
GC::MarkedVector<Value> final_rows(vm.heap());
|
||||
|
||||
// 2. Let `finalColumns` be the new list, initially empty
|
||||
MarkedVector<Value> final_columns(vm.heap());
|
||||
GC::MarkedVector<Value> final_columns(vm.heap());
|
||||
|
||||
HashMap<PropertyKey, bool> visited_columns;
|
||||
|
||||
|
@ -328,7 +328,7 @@ ThrowCompletionOr<Value> Console::table()
|
|||
TRY(final_data->set(PropertyKey("columns"), table_cols, Object::ShouldThrowExceptions::No));
|
||||
|
||||
// 5.4. Perform `Printer("table", finalData)`
|
||||
MarkedVector<Value> args(vm.heap());
|
||||
GC::MarkedVector<Value> args(vm.heap());
|
||||
args.append(Value(final_data));
|
||||
return m_client->printer(LogLevel::Table, args);
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ ThrowCompletionOr<Value> Console::dir()
|
|||
|
||||
// 2. Perform Printer("dir", « object », options).
|
||||
if (m_client) {
|
||||
MarkedVector<Value> printer_arguments { vm.heap() };
|
||||
GC::MarkedVector<Value> printer_arguments { vm.heap() };
|
||||
TRY_OR_THROW_OOM(vm, printer_arguments.try_append(object));
|
||||
|
||||
return m_client->printer(LogLevel::Dir, move(printer_arguments));
|
||||
|
@ -430,7 +430,7 @@ ThrowCompletionOr<Value> Console::count()
|
|||
auto concat = TRY_OR_THROW_OOM(vm, String::formatted("{}: {}", label, map.get(label).value()));
|
||||
|
||||
// 5. Perform Logger("count", « concat »).
|
||||
MarkedVector<Value> concat_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> concat_as_vector { vm.heap() };
|
||||
concat_as_vector.append(PrimitiveString::create(vm, move(concat)));
|
||||
if (m_client)
|
||||
TRY(m_client->logger(LogLevel::Count, concat_as_vector));
|
||||
|
@ -458,7 +458,7 @@ ThrowCompletionOr<Value> Console::count_reset()
|
|||
// that the given label does not have an associated count.
|
||||
auto message = TRY_OR_THROW_OOM(vm, String::formatted("\"{}\" doesn't have a count", label));
|
||||
// 2. Perform Logger("countReset", « message »);
|
||||
MarkedVector<Value> message_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> message_as_vector { vm.heap() };
|
||||
message_as_vector.append(PrimitiveString::create(vm, move(message)));
|
||||
if (m_client)
|
||||
TRY(m_client->logger(LogLevel::CountReset, message_as_vector));
|
||||
|
@ -561,7 +561,7 @@ ThrowCompletionOr<Value> Console::time()
|
|||
// a warning to the console indicating that a timer with label `label` has already been started.
|
||||
if (m_timer_table.contains(label)) {
|
||||
if (m_client) {
|
||||
MarkedVector<Value> timer_already_exists_warning_message_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> timer_already_exists_warning_message_as_vector { vm.heap() };
|
||||
|
||||
auto message = TRY_OR_THROW_OOM(vm, String::formatted("Timer '{}' already exists.", label));
|
||||
timer_already_exists_warning_message_as_vector.append(PrimitiveString::create(vm, move(message)));
|
||||
|
@ -592,7 +592,7 @@ ThrowCompletionOr<Value> Console::time_log()
|
|||
// NOTE: Warn if the timer doesn't exist. Not part of the spec yet, but discussed here: https://github.com/whatwg/console/issues/134
|
||||
if (maybe_start_time == m_timer_table.end()) {
|
||||
if (m_client) {
|
||||
MarkedVector<Value> timer_does_not_exist_warning_message_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> timer_does_not_exist_warning_message_as_vector { vm.heap() };
|
||||
|
||||
auto message = TRY_OR_THROW_OOM(vm, String::formatted("Timer '{}' does not exist.", label));
|
||||
timer_does_not_exist_warning_message_as_vector.append(PrimitiveString::create(vm, move(message)));
|
||||
|
@ -610,7 +610,7 @@ ThrowCompletionOr<Value> Console::time_log()
|
|||
auto concat = TRY_OR_THROW_OOM(vm, String::formatted("{}: {}", label, duration));
|
||||
|
||||
// 5. Prepend concat to data.
|
||||
MarkedVector<Value> data { vm.heap() };
|
||||
GC::MarkedVector<Value> data { vm.heap() };
|
||||
data.ensure_capacity(vm.argument_count());
|
||||
data.append(PrimitiveString::create(vm, move(concat)));
|
||||
for (size_t i = 1; i < vm.argument_count(); ++i)
|
||||
|
@ -638,7 +638,7 @@ ThrowCompletionOr<Value> Console::time_end()
|
|||
// NOTE: Warn if the timer doesn't exist. Not part of the spec yet, but discussed here: https://github.com/whatwg/console/issues/134
|
||||
if (maybe_start_time == m_timer_table.end()) {
|
||||
if (m_client) {
|
||||
MarkedVector<Value> timer_does_not_exist_warning_message_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> timer_does_not_exist_warning_message_as_vector { vm.heap() };
|
||||
|
||||
auto message = TRY_OR_THROW_OOM(vm, String::formatted("Timer '{}' does not exist.", label));
|
||||
timer_does_not_exist_warning_message_as_vector.append(PrimitiveString::create(vm, move(message)));
|
||||
|
@ -660,18 +660,18 @@ ThrowCompletionOr<Value> Console::time_end()
|
|||
|
||||
// 6. Perform Printer("timeEnd", « concat »).
|
||||
if (m_client) {
|
||||
MarkedVector<Value> concat_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> concat_as_vector { vm.heap() };
|
||||
concat_as_vector.append(PrimitiveString::create(vm, move(concat)));
|
||||
TRY(m_client->printer(LogLevel::TimeEnd, move(concat_as_vector)));
|
||||
}
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
MarkedVector<Value> Console::vm_arguments()
|
||||
GC::MarkedVector<Value> Console::vm_arguments()
|
||||
{
|
||||
auto& vm = realm().vm();
|
||||
|
||||
MarkedVector<Value> arguments { vm.heap() };
|
||||
GC::MarkedVector<Value> arguments { vm.heap() };
|
||||
arguments.ensure_capacity(vm.argument_count());
|
||||
for (size_t i = 0; i < vm.argument_count(); ++i) {
|
||||
arguments.append(vm.argument(i));
|
||||
|
@ -709,7 +709,7 @@ void Console::report_exception(JS::Error const& exception, bool in_promise) cons
|
|||
m_client->report_exception(exception, in_promise);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<String> Console::value_vector_to_string(MarkedVector<Value> const& values)
|
||||
ThrowCompletionOr<String> Console::value_vector_to_string(GC::MarkedVector<Value> const& values)
|
||||
{
|
||||
auto& vm = realm().vm();
|
||||
StringBuilder builder;
|
||||
|
@ -767,7 +767,7 @@ void ConsoleClient::visit_edges(Visitor& visitor)
|
|||
}
|
||||
|
||||
// 2.1. Logger(logLevel, args), https://console.spec.whatwg.org/#logger
|
||||
ThrowCompletionOr<Value> ConsoleClient::logger(Console::LogLevel log_level, MarkedVector<Value> const& args)
|
||||
ThrowCompletionOr<Value> ConsoleClient::logger(Console::LogLevel log_level, GC::MarkedVector<Value> const& args)
|
||||
{
|
||||
auto& vm = m_console->realm().vm();
|
||||
|
||||
|
@ -783,7 +783,7 @@ ThrowCompletionOr<Value> ConsoleClient::logger(Console::LogLevel log_level, Mark
|
|||
|
||||
// 4. If rest is empty, perform Printer(logLevel, « first ») and return.
|
||||
if (rest_size == 0) {
|
||||
MarkedVector<Value> first_as_vector { vm.heap() };
|
||||
GC::MarkedVector<Value> first_as_vector { vm.heap() };
|
||||
first_as_vector.append(first);
|
||||
return printer(log_level, move(first_as_vector));
|
||||
}
|
||||
|
@ -799,7 +799,7 @@ ThrowCompletionOr<Value> ConsoleClient::logger(Console::LogLevel log_level, Mark
|
|||
}
|
||||
|
||||
// 2.2. Formatter(args), https://console.spec.whatwg.org/#formatter
|
||||
ThrowCompletionOr<MarkedVector<Value>> ConsoleClient::formatter(MarkedVector<Value> const& args)
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> ConsoleClient::formatter(GC::MarkedVector<Value> const& args)
|
||||
{
|
||||
auto& realm = m_console->realm();
|
||||
auto& vm = realm.vm();
|
||||
|
@ -901,7 +901,7 @@ ThrowCompletionOr<MarkedVector<Value>> ConsoleClient::formatter(MarkedVector<Val
|
|||
}
|
||||
|
||||
// 7. Let result be a list containing target together with the elements of args starting from the third onward.
|
||||
MarkedVector<Value> result { vm.heap() };
|
||||
GC::MarkedVector<Value> result { vm.heap() };
|
||||
result.ensure_capacity(args.size() - 1);
|
||||
result.empend(PrimitiveString::create(vm, move(target)));
|
||||
for (size_t i = 2; i < args.size(); ++i)
|
||||
|
@ -911,7 +911,7 @@ ThrowCompletionOr<MarkedVector<Value>> ConsoleClient::formatter(MarkedVector<Val
|
|||
return formatter(result);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<String> ConsoleClient::generically_format_values(MarkedVector<Value> const& values)
|
||||
ThrowCompletionOr<String> ConsoleClient::generically_format_values(GC::MarkedVector<Value> const& values)
|
||||
{
|
||||
AllocatingMemoryStream stream;
|
||||
auto& vm = m_console->realm().vm();
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/ElapsedTimer.h>
|
||||
#include <LibGC/CellAllocator.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibJS/Heap/CellAllocator.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS {
|
||||
|
@ -24,8 +24,8 @@ class ConsoleClient;
|
|||
|
||||
// https://console.spec.whatwg.org
|
||||
class Console : public Cell {
|
||||
JS_CELL(Console, Cell);
|
||||
JS_DECLARE_ALLOCATOR(Console);
|
||||
GC_CELL(Console, Cell);
|
||||
GC_DECLARE_ALLOCATOR(Console);
|
||||
|
||||
public:
|
||||
virtual ~Console() override;
|
||||
|
@ -63,7 +63,7 @@ public:
|
|||
|
||||
Realm& realm() const { return m_realm; }
|
||||
|
||||
MarkedVector<Value> vm_arguments();
|
||||
GC::MarkedVector<Value> vm_arguments();
|
||||
|
||||
HashMap<String, unsigned>& counters() { return m_counters; }
|
||||
HashMap<String, unsigned> const& counters() const { return m_counters; }
|
||||
|
@ -95,11 +95,11 @@ private:
|
|||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
ThrowCompletionOr<String> value_vector_to_string(MarkedVector<Value> const&);
|
||||
ThrowCompletionOr<String> value_vector_to_string(GC::MarkedVector<Value> const&);
|
||||
ThrowCompletionOr<String> format_time_since(Core::ElapsedTimer timer);
|
||||
|
||||
NonnullGCPtr<Realm> m_realm;
|
||||
GCPtr<ConsoleClient> m_client;
|
||||
GC::Ref<Realm> m_realm;
|
||||
GC::Ptr<ConsoleClient> m_client;
|
||||
|
||||
HashMap<String, unsigned> m_counters;
|
||||
HashMap<String, Core::ElapsedTimer> m_timer_table;
|
||||
|
@ -107,14 +107,14 @@ private:
|
|||
};
|
||||
|
||||
class ConsoleClient : public Cell {
|
||||
JS_CELL(ConsoleClient, Cell);
|
||||
JS_DECLARE_ALLOCATOR(ConsoleClient);
|
||||
GC_CELL(ConsoleClient, Cell);
|
||||
GC_DECLARE_ALLOCATOR(ConsoleClient);
|
||||
|
||||
public:
|
||||
using PrinterArguments = Variant<Console::Group, Console::Trace, MarkedVector<Value>>;
|
||||
using PrinterArguments = Variant<Console::Group, Console::Trace, GC::MarkedVector<Value>>;
|
||||
|
||||
ThrowCompletionOr<Value> logger(Console::LogLevel log_level, MarkedVector<Value> const& args);
|
||||
ThrowCompletionOr<MarkedVector<Value>> formatter(MarkedVector<Value> const& args);
|
||||
ThrowCompletionOr<Value> logger(Console::LogLevel log_level, GC::MarkedVector<Value> const& args);
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> formatter(GC::MarkedVector<Value> const& args);
|
||||
virtual ThrowCompletionOr<Value> printer(Console::LogLevel log_level, PrinterArguments) = 0;
|
||||
|
||||
virtual void add_css_style_to_current_message(StringView) { }
|
||||
|
@ -123,14 +123,14 @@ public:
|
|||
virtual void clear() = 0;
|
||||
virtual void end_group() = 0;
|
||||
|
||||
ThrowCompletionOr<String> generically_format_values(MarkedVector<Value> const&);
|
||||
ThrowCompletionOr<String> generically_format_values(GC::MarkedVector<Value> const&);
|
||||
|
||||
protected:
|
||||
explicit ConsoleClient(Console&);
|
||||
virtual ~ConsoleClient() override;
|
||||
virtual void visit_edges(Visitor& visitor) override;
|
||||
|
||||
NonnullGCPtr<Console> m_console;
|
||||
GC::Ref<Console> m_console;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
namespace JS::Test262 {
|
||||
|
||||
JS_DEFINE_ALLOCATOR($262Object);
|
||||
GC_DEFINE_ALLOCATOR($262Object);
|
||||
|
||||
$262Object::$262Object(Realm& realm)
|
||||
: Object(Object::ConstructWithoutPrototypeTag::Tag, realm)
|
||||
|
@ -62,7 +62,7 @@ JS_DEFINE_NATIVE_FUNCTION($262Object::clear_kept_objects)
|
|||
|
||||
JS_DEFINE_NATIVE_FUNCTION($262Object::create_realm)
|
||||
{
|
||||
JS::GCPtr<JS::Test262::GlobalObject> global_object;
|
||||
GC::Ptr<JS::Test262::GlobalObject> global_object;
|
||||
auto root_execution_context = MUST(JS::Realm::initialize_host_defined_realm(
|
||||
vm,
|
||||
[&](JS::Realm& realm) -> JS::GlobalObject* {
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace JS::Test262 {
|
|||
|
||||
class $262Object final : public Object {
|
||||
JS_OBJECT($262Object, Object);
|
||||
JS_DECLARE_ALLOCATOR($262Object);
|
||||
GC_DECLARE_ALLOCATOR($262Object);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
@ -26,8 +26,8 @@ private:
|
|||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
GCPtr<AgentObject> m_agent;
|
||||
GCPtr<IsHTMLDDA> m_is_htmldda;
|
||||
GC::Ptr<AgentObject> m_agent;
|
||||
GC::Ptr<IsHTMLDDA> m_is_htmldda;
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(clear_kept_objects);
|
||||
JS_DECLARE_NATIVE_FUNCTION(create_realm);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace JS::Test262 {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AgentObject);
|
||||
GC_DEFINE_ALLOCATOR(AgentObject);
|
||||
|
||||
AgentObject::AgentObject(Realm& realm)
|
||||
: Object(Object::ConstructWithoutPrototypeTag::Tag, realm)
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace JS::Test262 {
|
|||
|
||||
class AgentObject final : public Object {
|
||||
JS_OBJECT(AgentObject, Object);
|
||||
JS_DECLARE_ALLOCATOR(AgentObject);
|
||||
GC_DECLARE_ALLOCATOR(AgentObject);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace JS::Test262 {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(GlobalObject);
|
||||
GC_DEFINE_ALLOCATOR(GlobalObject);
|
||||
|
||||
void GlobalObject::initialize(Realm& realm)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace JS::Test262 {
|
|||
|
||||
class GlobalObject final : public JS::GlobalObject {
|
||||
JS_OBJECT(GlobalObject, JS::GlobalObject);
|
||||
JS_DECLARE_ALLOCATOR(GlobalObject);
|
||||
GC_DECLARE_ALLOCATOR(GlobalObject);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
@ -29,7 +29,7 @@ private:
|
|||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
GCPtr<$262Object> m_$262;
|
||||
GC::Ptr<$262Object> m_$262;
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(print);
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
namespace JS::Test262 {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(IsHTMLDDA);
|
||||
GC_DEFINE_ALLOCATOR(IsHTMLDDA);
|
||||
|
||||
IsHTMLDDA::IsHTMLDDA(Realm& realm)
|
||||
// NativeFunction without prototype is currently not possible (only due to the lack of a ctor that supports it)
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace JS::Test262 {
|
|||
|
||||
class IsHTMLDDA final : public NativeFunction {
|
||||
JS_OBJECT(IsHTMLDDA, NativeFunction);
|
||||
JS_DECLARE_ALLOCATOR(IsHTMLDDA);
|
||||
GC_DECLARE_ALLOCATOR(IsHTMLDDA);
|
||||
|
||||
public:
|
||||
virtual ~IsHTMLDDA() override = default;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(CyclicModule);
|
||||
GC_DEFINE_ALLOCATOR(CyclicModule);
|
||||
|
||||
CyclicModule::CyclicModule(Realm& realm, StringView filename, bool has_top_level_await, Vector<ModuleRequest> requested_modules, Script::HostDefined* host_defined)
|
||||
: Module(realm, filename, host_defined)
|
||||
|
@ -45,7 +45,7 @@ void GraphLoadingState::visit_edges(Cell::Visitor& visitor)
|
|||
}
|
||||
|
||||
// 16.2.1.5.1 LoadRequestedModules ( [ hostDefined ] ), https://tc39.es/ecma262/#sec-LoadRequestedModules
|
||||
PromiseCapability& CyclicModule::load_requested_modules(GCPtr<GraphLoadingState::HostDefined> host_defined)
|
||||
PromiseCapability& CyclicModule::load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined> host_defined)
|
||||
{
|
||||
// 1. If hostDefined is not present, let hostDefined be EMPTY.
|
||||
// NOTE: The empty state is handled by hostDefined being an optional without value.
|
||||
|
@ -54,7 +54,7 @@ PromiseCapability& CyclicModule::load_requested_modules(GCPtr<GraphLoadingState:
|
|||
auto promise_capability = MUST(new_promise_capability(vm(), vm().current_realm()->intrinsics().promise_constructor()));
|
||||
|
||||
// 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true, [[PendingModulesCount]]: 1, [[Visited]]: « », [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }.
|
||||
auto state = heap().allocate<GraphLoadingState>(promise_capability, true, 1, HashTable<JS::GCPtr<CyclicModule>> {}, move(host_defined));
|
||||
auto state = heap().allocate<GraphLoadingState>(promise_capability, true, 1, HashTable<GC::Ptr<CyclicModule>> {}, move(host_defined));
|
||||
|
||||
// 4. Perform InnerModuleLoading(state, module).
|
||||
inner_module_loading(state);
|
||||
|
@ -101,7 +101,7 @@ void CyclicModule::inner_module_loading(JS::GraphLoadingState& state)
|
|||
// ii. Else,
|
||||
if (!found_record_in_loaded_modules) {
|
||||
// 1. Perform HostLoadImportedModule(module, required, state.[[HostDefined]], state).
|
||||
vm().host_load_imported_module(NonnullGCPtr<CyclicModule> { *this }, required, state.host_defined, NonnullGCPtr<GraphLoadingState> { state });
|
||||
vm().host_load_imported_module(GC::Ref<CyclicModule> { *this }, required, state.host_defined, GC::Ref<GraphLoadingState> { state });
|
||||
|
||||
// 2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters the graph loading process through ContinueModuleLoading.
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ void CyclicModule::inner_module_loading(JS::GraphLoadingState& state)
|
|||
}
|
||||
|
||||
// 16.2.1.5.1.2 ContinueModuleLoading ( state, moduleCompletion ), https://tc39.es/ecma262/#sec-ContinueModuleLoading
|
||||
void continue_module_loading(GraphLoadingState& state, ThrowCompletionOr<NonnullGCPtr<Module>> const& module_completion)
|
||||
void continue_module_loading(GraphLoadingState& state, ThrowCompletionOr<GC::Ref<Module>> const& module_completion)
|
||||
{
|
||||
// 1. If state.[[IsLoading]] is false, return UNUSED.
|
||||
if (!state.is_loading)
|
||||
|
@ -469,7 +469,7 @@ ThrowCompletionOr<u32> CyclicModule::inner_module_evaluation(VM& vm, Vector<Modu
|
|||
if (!is<CyclicModule>(*required_module))
|
||||
continue;
|
||||
|
||||
JS::NonnullGCPtr<CyclicModule> cyclic_module = verify_cast<CyclicModule>(*required_module);
|
||||
GC::Ref<CyclicModule> cyclic_module = verify_cast<CyclicModule>(*required_module);
|
||||
// i. Assert: requiredModule.[[Status]] is either evaluating, evaluating-async, or evaluated.
|
||||
VERIFY(cyclic_module->m_status == ModuleStatus::Evaluating || cyclic_module->m_status == ModuleStatus::EvaluatingAsync || cyclic_module->m_status == ModuleStatus::Evaluated);
|
||||
|
||||
|
@ -578,7 +578,7 @@ ThrowCompletionOr<void> CyclicModule::initialize_environment(VM&)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CyclicModule::execute_module(VM&, GCPtr<PromiseCapability>)
|
||||
ThrowCompletionOr<void> CyclicModule::execute_module(VM&, GC::Ptr<PromiseCapability>)
|
||||
{
|
||||
// Note: In ecma262 this is never called on a cyclic module only on SourceTextModules.
|
||||
// So this check is to make sure we don't accidentally call this.
|
||||
|
@ -807,7 +807,7 @@ void CyclicModule::async_module_execution_rejected(VM& vm, Value error)
|
|||
}
|
||||
|
||||
// 16.2.1.7 GetImportedModule ( referrer, specifier ), https://tc39.es/ecma262/#sec-GetImportedModule
|
||||
NonnullGCPtr<Module> CyclicModule::get_imported_module(ModuleRequest const& request)
|
||||
GC::Ref<Module> CyclicModule::get_imported_module(ModuleRequest const& request)
|
||||
{
|
||||
// 1. Assert: Exactly one element of referrer.[[LoadedModules]] is a Record whose [[Specifier]] is specifier,
|
||||
// since LoadRequestedModules has completed successfully on referrer prior to invoking this abstract operation.
|
||||
|
@ -829,7 +829,7 @@ NonnullGCPtr<Module> CyclicModule::get_imported_module(ModuleRequest const& requ
|
|||
}
|
||||
|
||||
// 13.3.10.1.1 ContinueDynamicImport ( promiseCapability, moduleCompletion ), https://tc39.es/ecma262/#sec-ContinueDynamicImport
|
||||
void continue_dynamic_import(NonnullGCPtr<PromiseCapability> promise_capability, ThrowCompletionOr<NonnullGCPtr<Module>> const& module_completion)
|
||||
void continue_dynamic_import(GC::Ref<PromiseCapability> promise_capability, ThrowCompletionOr<GC::Ref<Module>> const& module_completion)
|
||||
{
|
||||
auto& vm = promise_capability->vm();
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ enum class ModuleStatus {
|
|||
|
||||
// 16.2.1.5 Cyclic Module Records, https://tc39.es/ecma262/#cyclic-module-record
|
||||
class CyclicModule : public Module {
|
||||
JS_CELL(CyclicModule, Module);
|
||||
JS_DECLARE_ALLOCATOR(CyclicModule);
|
||||
GC_CELL(CyclicModule, Module);
|
||||
GC_DECLARE_ALLOCATOR(CyclicModule);
|
||||
|
||||
public:
|
||||
virtual ~CyclicModule() override;
|
||||
|
@ -35,7 +35,7 @@ public:
|
|||
virtual ThrowCompletionOr<void> link(VM& vm) override final;
|
||||
virtual ThrowCompletionOr<Promise*> evaluate(VM& vm) override final;
|
||||
|
||||
virtual PromiseCapability& load_requested_modules(GCPtr<GraphLoadingState::HostDefined>) override;
|
||||
virtual PromiseCapability& load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined>) override;
|
||||
virtual void inner_module_loading(GraphLoadingState& state);
|
||||
|
||||
Vector<ModuleRequest> const& requested_modules() const { return m_requested_modules; }
|
||||
|
@ -51,30 +51,30 @@ protected:
|
|||
virtual ThrowCompletionOr<u32> inner_module_evaluation(VM& vm, Vector<Module*>& stack, u32 index) override final;
|
||||
|
||||
virtual ThrowCompletionOr<void> initialize_environment(VM& vm);
|
||||
virtual ThrowCompletionOr<void> execute_module(VM& vm, GCPtr<PromiseCapability> capability = {});
|
||||
virtual ThrowCompletionOr<void> execute_module(VM& vm, GC::Ptr<PromiseCapability> capability = {});
|
||||
|
||||
[[nodiscard]] NonnullGCPtr<Module> get_imported_module(ModuleRequest const&);
|
||||
[[nodiscard]] GC::Ref<Module> get_imported_module(ModuleRequest const&);
|
||||
|
||||
void execute_async_module(VM& vm);
|
||||
void gather_available_ancestors(Vector<CyclicModule*>& exec_list);
|
||||
void async_module_execution_fulfilled(VM& vm);
|
||||
void async_module_execution_rejected(VM& vm, Value error);
|
||||
|
||||
ModuleStatus m_status { ModuleStatus::New }; // [[Status]]
|
||||
ThrowCompletionOr<void> m_evaluation_error; // [[EvaluationError]]
|
||||
Optional<u32> m_dfs_index; // [[DFSIndex]]
|
||||
Optional<u32> m_dfs_ancestor_index; // [[DFSAncestorIndex]]
|
||||
Vector<ModuleRequest> m_requested_modules; // [[RequestedModules]]
|
||||
Vector<ModuleWithSpecifier> m_loaded_modules; // [[LoadedModules]]
|
||||
GCPtr<CyclicModule> m_cycle_root; // [[CycleRoot]]
|
||||
bool m_has_top_level_await { false }; // [[HasTLA]]
|
||||
bool m_async_evaluation { false }; // [[AsyncEvaluation]]
|
||||
GCPtr<PromiseCapability> m_top_level_capability; // [[TopLevelCapability]]
|
||||
Vector<GCPtr<CyclicModule>> m_async_parent_modules; // [[AsyncParentModules]]
|
||||
Optional<u32> m_pending_async_dependencies; // [[PendingAsyncDependencies]]
|
||||
ModuleStatus m_status { ModuleStatus::New }; // [[Status]]
|
||||
ThrowCompletionOr<void> m_evaluation_error; // [[EvaluationError]]
|
||||
Optional<u32> m_dfs_index; // [[DFSIndex]]
|
||||
Optional<u32> m_dfs_ancestor_index; // [[DFSAncestorIndex]]
|
||||
Vector<ModuleRequest> m_requested_modules; // [[RequestedModules]]
|
||||
Vector<ModuleWithSpecifier> m_loaded_modules; // [[LoadedModules]]
|
||||
GC::Ptr<CyclicModule> m_cycle_root; // [[CycleRoot]]
|
||||
bool m_has_top_level_await { false }; // [[HasTLA]]
|
||||
bool m_async_evaluation { false }; // [[AsyncEvaluation]]
|
||||
GC::Ptr<PromiseCapability> m_top_level_capability; // [[TopLevelCapability]]
|
||||
Vector<GC::Ptr<CyclicModule>> m_async_parent_modules; // [[AsyncParentModules]]
|
||||
Optional<u32> m_pending_async_dependencies; // [[PendingAsyncDependencies]]
|
||||
};
|
||||
|
||||
void continue_module_loading(GraphLoadingState&, ThrowCompletionOr<NonnullGCPtr<Module>> const&);
|
||||
void continue_dynamic_import(NonnullGCPtr<PromiseCapability>, ThrowCompletionOr<NonnullGCPtr<Module>> const& module_completion);
|
||||
void continue_module_loading(GraphLoadingState&, ThrowCompletionOr<GC::Ref<Module>> const&);
|
||||
void continue_dynamic_import(GC::Ref<PromiseCapability>, ThrowCompletionOr<GC::Ref<Module>> const& module_completion);
|
||||
|
||||
}
|
||||
|
|
|
@ -159,15 +159,12 @@ class BigInt;
|
|||
class BoundFunction;
|
||||
struct CachedSourceRange;
|
||||
class Cell;
|
||||
class CellImpl;
|
||||
class CellAllocator;
|
||||
class ClassExpression;
|
||||
struct ClassFieldDefinition;
|
||||
class Completion;
|
||||
class Console;
|
||||
class CyclicModule;
|
||||
class DeclarativeEnvironment;
|
||||
class DeferGC;
|
||||
class ECMAScriptFunctionObject;
|
||||
class Environment;
|
||||
class Error;
|
||||
|
@ -183,9 +180,6 @@ struct FunctionParameter;
|
|||
class GlobalEnvironment;
|
||||
class GlobalObject;
|
||||
struct GraphLoadingState;
|
||||
class HandleImpl;
|
||||
class Heap;
|
||||
class HeapBlock;
|
||||
struct ImportEntry;
|
||||
class ImportStatement;
|
||||
class Identifier;
|
||||
|
@ -195,7 +189,6 @@ class MemberExpression;
|
|||
class MetaProperty;
|
||||
class Module;
|
||||
struct ModuleRequest;
|
||||
class NanBoxedValue;
|
||||
class NativeFunction;
|
||||
class ObjectEnvironment;
|
||||
class Parser;
|
||||
|
@ -223,7 +216,6 @@ class Utf16String;
|
|||
class VM;
|
||||
class PrototypeChainValidity;
|
||||
class Value;
|
||||
class WeakContainer;
|
||||
class WrappedFunction;
|
||||
enum class DeclarationKind;
|
||||
struct AlreadyResolved;
|
||||
|
@ -301,22 +293,10 @@ struct TimeZoneMethods;
|
|||
struct PartialDurationRecord;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class HeapFunction;
|
||||
|
||||
template<typename T>
|
||||
requires(!IsLvalueReference<T>)
|
||||
class ThrowCompletionOr;
|
||||
|
||||
template<class T>
|
||||
class Handle;
|
||||
|
||||
template<class T, size_t inline_capacity = 0>
|
||||
class ConservativeVector;
|
||||
|
||||
template<class T, size_t inline_capacity = 0>
|
||||
class MarkedVector;
|
||||
|
||||
namespace Bytecode {
|
||||
class BasicBlock;
|
||||
enum class Builtin : u8;
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Heap/BlockAllocator.h>
|
||||
#include <LibJS/Heap/HeapBlock.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifdef HAS_ADDRESS_SANITIZER
|
||||
# include <sanitizer/asan_interface.h>
|
||||
# include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
|
||||
#if defined(AK_OS_GNU_HURD) || (!defined(MADV_FREE) && !defined(MADV_DONTNEED))
|
||||
# define USE_FALLBACK_BLOCK_DEALLOCATION
|
||||
#endif
|
||||
|
||||
namespace JS {
|
||||
|
||||
BlockAllocator::~BlockAllocator()
|
||||
{
|
||||
for (auto* block : m_blocks) {
|
||||
ASAN_UNPOISON_MEMORY_REGION(block, HeapBlock::block_size);
|
||||
if (munmap(block, HeapBlock::block_size) < 0) {
|
||||
perror("munmap");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* BlockAllocator::allocate_block([[maybe_unused]] char const* name)
|
||||
{
|
||||
if (!m_blocks.is_empty()) {
|
||||
// To reduce predictability, take a random block from the cache.
|
||||
size_t random_index = get_random_uniform(m_blocks.size());
|
||||
auto* block = m_blocks.unstable_take(random_index);
|
||||
ASAN_UNPOISON_MEMORY_REGION(block, HeapBlock::block_size);
|
||||
LSAN_REGISTER_ROOT_REGION(block, HeapBlock::block_size);
|
||||
return block;
|
||||
}
|
||||
|
||||
auto* block = (HeapBlock*)mmap(nullptr, HeapBlock::block_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
VERIFY(block != MAP_FAILED);
|
||||
LSAN_REGISTER_ROOT_REGION(block, HeapBlock::block_size);
|
||||
return block;
|
||||
}
|
||||
|
||||
void BlockAllocator::deallocate_block(void* block)
|
||||
{
|
||||
VERIFY(block);
|
||||
|
||||
#if defined(USE_FALLBACK_BLOCK_DEALLOCATION)
|
||||
// If we can't use any of the nicer techniques, unmap and remap the block to return the physical pages while keeping the VM.
|
||||
if (munmap(block, HeapBlock::block_size) < 0) {
|
||||
perror("munmap");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
if (mmap(block, HeapBlock::block_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0) != block) {
|
||||
perror("mmap");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#elif defined(MADV_FREE)
|
||||
if (madvise(block, HeapBlock::block_size, MADV_FREE) < 0) {
|
||||
perror("madvise(MADV_FREE)");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#elif defined(MADV_DONTNEED)
|
||||
if (madvise(block, HeapBlock::block_size, MADV_DONTNEED) < 0) {
|
||||
perror("madvise(MADV_DONTNEED)");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#endif
|
||||
|
||||
ASAN_POISON_MEMORY_REGION(block, HeapBlock::block_size);
|
||||
LSAN_UNREGISTER_ROOT_REGION(block, HeapBlock::block_size);
|
||||
m_blocks.append(block);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class BlockAllocator {
|
||||
public:
|
||||
BlockAllocator() = default;
|
||||
~BlockAllocator();
|
||||
|
||||
void* allocate_block(char const* name);
|
||||
void deallocate_block(void*);
|
||||
|
||||
private:
|
||||
Vector<void*> m_blocks;
|
||||
};
|
||||
|
||||
}
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibGC/Cell.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class Cell : public CellImpl {
|
||||
JS_CELL(Cell, CellImpl);
|
||||
class Cell : public GC::Cell {
|
||||
GC_CELL(Cell, GC::Cell);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&);
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibJS/Heap/BlockAllocator.h>
|
||||
#include <LibJS/Heap/CellAllocator.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Heap/HeapBlock.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
CellAllocator::CellAllocator(size_t cell_size, char const* class_name)
|
||||
: m_class_name(class_name)
|
||||
, m_cell_size(cell_size)
|
||||
{
|
||||
}
|
||||
|
||||
CellImpl* CellAllocator::allocate_cell(Heap& heap)
|
||||
{
|
||||
if (!m_list_node.is_in_list())
|
||||
heap.register_cell_allocator({}, *this);
|
||||
|
||||
if (m_usable_blocks.is_empty()) {
|
||||
auto block = HeapBlock::create_with_cell_size(heap, *this, m_cell_size, m_class_name);
|
||||
auto block_ptr = reinterpret_cast<FlatPtr>(block.ptr());
|
||||
if (m_min_block_address > block_ptr)
|
||||
m_min_block_address = block_ptr;
|
||||
if (m_max_block_address < block_ptr)
|
||||
m_max_block_address = block_ptr;
|
||||
m_usable_blocks.append(*block.leak_ptr());
|
||||
}
|
||||
|
||||
auto& block = *m_usable_blocks.last();
|
||||
auto* cell = block.allocate();
|
||||
VERIFY(cell);
|
||||
if (block.is_full())
|
||||
m_full_blocks.append(*m_usable_blocks.last());
|
||||
return cell;
|
||||
}
|
||||
|
||||
void CellAllocator::block_did_become_empty(Badge<Heap>, HeapBlock& block)
|
||||
{
|
||||
block.m_list_node.remove();
|
||||
// NOTE: HeapBlocks are managed by the BlockAllocator, so we don't want to `delete` the block here.
|
||||
block.~HeapBlock();
|
||||
m_block_allocator.deallocate_block(&block);
|
||||
}
|
||||
|
||||
void CellAllocator::block_did_become_usable(Badge<Heap>, HeapBlock& block)
|
||||
{
|
||||
VERIFY(!block.is_full());
|
||||
m_usable_blocks.append(block);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/NeverDestroyed.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/BlockAllocator.h>
|
||||
#include <LibJS/Heap/HeapBlock.h>
|
||||
|
||||
#define JS_DECLARE_ALLOCATOR(ClassName) \
|
||||
static JS::TypeIsolatingCellAllocator<ClassName> cell_allocator
|
||||
|
||||
#define JS_DEFINE_ALLOCATOR(ClassName) \
|
||||
JS::TypeIsolatingCellAllocator<ClassName> ClassName::cell_allocator { #ClassName }
|
||||
|
||||
namespace JS {
|
||||
|
||||
class CellAllocator {
|
||||
public:
|
||||
CellAllocator(size_t cell_size, char const* class_name = nullptr);
|
||||
~CellAllocator() = default;
|
||||
|
||||
size_t cell_size() const { return m_cell_size; }
|
||||
|
||||
CellImpl* allocate_cell(Heap&);
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_block(Callback callback)
|
||||
{
|
||||
for (auto& block : m_full_blocks) {
|
||||
if (callback(block) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
for (auto& block : m_usable_blocks) {
|
||||
if (callback(block) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
void block_did_become_empty(Badge<Heap>, HeapBlock&);
|
||||
void block_did_become_usable(Badge<Heap>, HeapBlock&);
|
||||
|
||||
IntrusiveListNode<CellAllocator> m_list_node;
|
||||
using List = IntrusiveList<&CellAllocator::m_list_node>;
|
||||
|
||||
BlockAllocator& block_allocator() { return m_block_allocator; }
|
||||
FlatPtr min_block_address() const { return m_min_block_address; }
|
||||
FlatPtr max_block_address() const { return m_max_block_address; }
|
||||
|
||||
private:
|
||||
char const* const m_class_name { nullptr };
|
||||
size_t const m_cell_size;
|
||||
|
||||
BlockAllocator m_block_allocator;
|
||||
|
||||
using BlockList = IntrusiveList<&HeapBlock::m_list_node>;
|
||||
BlockList m_full_blocks;
|
||||
BlockList m_usable_blocks;
|
||||
FlatPtr m_min_block_address { explode_byte(0xff) };
|
||||
FlatPtr m_max_block_address { 0 };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class TypeIsolatingCellAllocator {
|
||||
public:
|
||||
using CellType = T;
|
||||
|
||||
TypeIsolatingCellAllocator(char const* class_name)
|
||||
: allocator(sizeof(T), class_name)
|
||||
{
|
||||
}
|
||||
|
||||
NeverDestroyed<CellAllocator> allocator;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/NanBoxedValue.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
void JS::CellImpl::Visitor::visit(NanBoxedValue const& value)
|
||||
{
|
||||
if (value.is_cell())
|
||||
visit_impl(value.as_cell());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibJS/Heap/Internals.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
// This instrumentation tells analysis tooling to ignore a potentially mis-wrapped GC-allocated member variable
|
||||
// It should only be used when the lifetime of the GC-allocated member is always longer than the object
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# define IGNORE_GC [[clang::annotate("serenity::ignore_gc")]]
|
||||
#else
|
||||
# define IGNORE_GC
|
||||
#endif
|
||||
|
||||
#define JS_CELL(class_, base_class) \
|
||||
public: \
|
||||
using Base = base_class; \
|
||||
virtual StringView class_name() const override \
|
||||
{ \
|
||||
return #class_##sv; \
|
||||
} \
|
||||
friend class JS::Heap;
|
||||
|
||||
class CellImpl : public Weakable<CellImpl> {
|
||||
AK_MAKE_NONCOPYABLE(CellImpl);
|
||||
AK_MAKE_NONMOVABLE(CellImpl);
|
||||
|
||||
public:
|
||||
virtual ~CellImpl() = default;
|
||||
|
||||
bool is_marked() const { return m_mark; }
|
||||
void set_marked(bool b) { m_mark = b; }
|
||||
|
||||
enum class State : bool {
|
||||
Live,
|
||||
Dead,
|
||||
};
|
||||
|
||||
State state() const { return m_state; }
|
||||
void set_state(State state) { m_state = state; }
|
||||
|
||||
virtual StringView class_name() const = 0;
|
||||
|
||||
class Visitor {
|
||||
public:
|
||||
void visit(CellImpl* cell)
|
||||
{
|
||||
if (cell)
|
||||
visit_impl(*cell);
|
||||
}
|
||||
|
||||
void visit(CellImpl& cell)
|
||||
{
|
||||
visit_impl(cell);
|
||||
}
|
||||
|
||||
void visit(CellImpl const* cell)
|
||||
{
|
||||
visit(const_cast<CellImpl*>(cell));
|
||||
}
|
||||
|
||||
void visit(CellImpl const& cell)
|
||||
{
|
||||
visit(const_cast<CellImpl&>(cell));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(GCPtr<T> cell)
|
||||
{
|
||||
if (cell)
|
||||
visit_impl(const_cast<RemoveConst<T>&>(*cell.ptr()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(NonnullGCPtr<T> cell)
|
||||
{
|
||||
visit_impl(const_cast<RemoveConst<T>&>(*cell.ptr()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(ReadonlySpan<T> span)
|
||||
{
|
||||
for (auto& value : span)
|
||||
visit(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(Span<T> span)
|
||||
{
|
||||
for (auto& value : span)
|
||||
visit(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(Vector<T> const& vector)
|
||||
{
|
||||
for (auto& value : vector)
|
||||
visit(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(HashTable<T> const& table)
|
||||
{
|
||||
for (auto& value : table)
|
||||
visit(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(OrderedHashTable<T> const& table)
|
||||
{
|
||||
for (auto& value : table)
|
||||
visit(value);
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename T>
|
||||
void visit(HashMap<K, V, T> const& map)
|
||||
{
|
||||
for (auto& it : map) {
|
||||
if constexpr (requires { visit(it.key); })
|
||||
visit(it.key);
|
||||
if constexpr (requires { visit(it.value); })
|
||||
visit(it.value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename T>
|
||||
void visit(OrderedHashMap<K, V, T> const& map)
|
||||
{
|
||||
for (auto& it : map) {
|
||||
if constexpr (requires { visit(it.key); })
|
||||
visit(it.key);
|
||||
if constexpr (requires { visit(it.value); })
|
||||
visit(it.value);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(NanBoxedValue const& value);
|
||||
|
||||
// Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead
|
||||
// of just not using it.
|
||||
template<typename T>
|
||||
void ignore(T const&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void visit_possible_values(ReadonlyBytes) = 0;
|
||||
|
||||
protected:
|
||||
virtual void visit_impl(CellImpl&) = 0;
|
||||
virtual ~Visitor() = default;
|
||||
};
|
||||
|
||||
virtual void visit_edges(Visitor&) { }
|
||||
|
||||
// This will be called on unmarked objects by the garbage collector in a separate pass before destruction.
|
||||
virtual void finalize() { }
|
||||
|
||||
// This allows cells to survive GC by choice, even if nothing points to them.
|
||||
// It's used to implement special rules in the web platform.
|
||||
// NOTE: Cells must call set_overrides_must_survive_garbage_collection() for this to be honored.
|
||||
virtual bool must_survive_garbage_collection() const { return false; }
|
||||
|
||||
bool overrides_must_survive_garbage_collection(Badge<Heap>) const { return m_overrides_must_survive_garbage_collection; }
|
||||
|
||||
ALWAYS_INLINE Heap& heap() const { return HeapBlockBase::from_cell(this)->heap(); }
|
||||
|
||||
protected:
|
||||
CellImpl() = default;
|
||||
|
||||
ALWAYS_INLINE void* private_data() const { return bit_cast<HeapBase*>(&heap())->private_data(); }
|
||||
|
||||
void set_overrides_must_survive_garbage_collection(bool b) { m_overrides_must_survive_garbage_collection = b; }
|
||||
|
||||
private:
|
||||
bool m_mark : 1 { false };
|
||||
bool m_overrides_must_survive_garbage_collection : 1 { false };
|
||||
State m_state : 1 { State::Live };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<JS::CellImpl> : AK::Formatter<FormatString> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, JS::CellImpl const* cell)
|
||||
{
|
||||
if (!cell)
|
||||
return builder.put_string("Cell{nullptr}"sv);
|
||||
return Formatter<FormatString>::format(builder, "{}({})"sv, cell->class_name(), cell);
|
||||
}
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Heap/ConservativeVector.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
ConservativeVectorBase::ConservativeVectorBase(Heap& heap)
|
||||
: m_heap(&heap)
|
||||
{
|
||||
m_heap->did_create_conservative_vector({}, *this);
|
||||
}
|
||||
|
||||
ConservativeVectorBase::~ConservativeVectorBase()
|
||||
{
|
||||
m_heap->did_destroy_conservative_vector({}, *this);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/HeapRoot.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class ConservativeVectorBase {
|
||||
public:
|
||||
virtual ReadonlySpan<FlatPtr> possible_values() const = 0;
|
||||
|
||||
protected:
|
||||
explicit ConservativeVectorBase(Heap&);
|
||||
~ConservativeVectorBase();
|
||||
|
||||
ConservativeVectorBase& operator=(ConservativeVectorBase const&);
|
||||
|
||||
Heap* m_heap { nullptr };
|
||||
IntrusiveListNode<ConservativeVectorBase> m_list_node;
|
||||
|
||||
public:
|
||||
using List = IntrusiveList<&ConservativeVectorBase::m_list_node>;
|
||||
};
|
||||
|
||||
template<typename T, size_t inline_capacity>
|
||||
class ConservativeVector final
|
||||
: public ConservativeVectorBase
|
||||
, public Vector<T, inline_capacity> {
|
||||
|
||||
public:
|
||||
explicit ConservativeVector(Heap& heap)
|
||||
: ConservativeVectorBase(heap)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ConservativeVector() = default;
|
||||
|
||||
ConservativeVector(ConservativeVector const& other)
|
||||
: ConservativeVectorBase(*other.m_heap)
|
||||
, Vector<T, inline_capacity>(other)
|
||||
{
|
||||
}
|
||||
|
||||
ConservativeVector(ConservativeVector&& other)
|
||||
: ConservativeVectorBase(*other.m_heap)
|
||||
, Vector<T, inline_capacity>(move(static_cast<Vector<T, inline_capacity>&>(other)))
|
||||
{
|
||||
}
|
||||
|
||||
ConservativeVector& operator=(ConservativeVector const& other)
|
||||
{
|
||||
Vector<T, inline_capacity>::operator=(other);
|
||||
ConservativeVectorBase::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ReadonlySpan<FlatPtr> possible_values() const override
|
||||
{
|
||||
static_assert(sizeof(T) >= sizeof(FlatPtr));
|
||||
return ReadonlySpan<FlatPtr> {
|
||||
reinterpret_cast<FlatPtr const*>(this->data()),
|
||||
this->size() * sizeof(T) / sizeof(FlatPtr),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class DeferGC {
|
||||
public:
|
||||
explicit DeferGC(Heap& heap)
|
||||
: m_heap(heap)
|
||||
{
|
||||
m_heap.defer_gc();
|
||||
}
|
||||
|
||||
~DeferGC()
|
||||
{
|
||||
m_heap.undefer_gc();
|
||||
}
|
||||
|
||||
private:
|
||||
Heap& m_heap;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Traits.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
template<typename T>
|
||||
class GCPtr;
|
||||
|
||||
template<typename T>
|
||||
class NonnullGCPtr {
|
||||
public:
|
||||
NonnullGCPtr() = delete;
|
||||
|
||||
NonnullGCPtr(T& ptr)
|
||||
: m_ptr(&ptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NonnullGCPtr(U& ptr)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
: m_ptr(&static_cast<T&>(ptr))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NonnullGCPtr(NonnullGCPtr<U> const& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
: m_ptr(other.ptr())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NonnullGCPtr& operator=(NonnullGCPtr<U> const& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
{
|
||||
m_ptr = static_cast<T*>(other.ptr());
|
||||
return *this;
|
||||
}
|
||||
|
||||
NonnullGCPtr& operator=(T& other)
|
||||
{
|
||||
m_ptr = &other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NonnullGCPtr& operator=(U& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
{
|
||||
m_ptr = &static_cast<T&>(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RETURNS_NONNULL T* operator->() const { return m_ptr; }
|
||||
|
||||
[[nodiscard]] T& operator*() const { return *m_ptr; }
|
||||
|
||||
RETURNS_NONNULL T* ptr() const { return m_ptr; }
|
||||
|
||||
RETURNS_NONNULL operator T*() const { return m_ptr; }
|
||||
|
||||
operator T&() const { return *m_ptr; }
|
||||
|
||||
private:
|
||||
T* m_ptr { nullptr };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class GCPtr {
|
||||
public:
|
||||
constexpr GCPtr() = default;
|
||||
|
||||
GCPtr(T& ptr)
|
||||
: m_ptr(&ptr)
|
||||
{
|
||||
}
|
||||
|
||||
GCPtr(T* ptr)
|
||||
: m_ptr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
GCPtr(GCPtr<U> const& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
: m_ptr(other.ptr())
|
||||
{
|
||||
}
|
||||
|
||||
GCPtr(NonnullGCPtr<T> const& other)
|
||||
: m_ptr(other.ptr())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
GCPtr(NonnullGCPtr<U> const& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
: m_ptr(other.ptr())
|
||||
{
|
||||
}
|
||||
|
||||
GCPtr(nullptr_t)
|
||||
: m_ptr(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
GCPtr& operator=(GCPtr<U> const& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
{
|
||||
m_ptr = static_cast<T*>(other.ptr());
|
||||
return *this;
|
||||
}
|
||||
|
||||
GCPtr& operator=(NonnullGCPtr<T> const& other)
|
||||
{
|
||||
m_ptr = other.ptr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
GCPtr& operator=(NonnullGCPtr<U> const& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
{
|
||||
m_ptr = static_cast<T*>(other.ptr());
|
||||
return *this;
|
||||
}
|
||||
|
||||
GCPtr& operator=(T& other)
|
||||
{
|
||||
m_ptr = &other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
GCPtr& operator=(U& other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
{
|
||||
m_ptr = &static_cast<T&>(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
GCPtr& operator=(T* other)
|
||||
{
|
||||
m_ptr = other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
GCPtr& operator=(U* other)
|
||||
requires(IsConvertible<U*, T*>)
|
||||
{
|
||||
m_ptr = static_cast<T*>(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
ASSERT(m_ptr);
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] T& operator*() const
|
||||
{
|
||||
ASSERT(m_ptr);
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
T* ptr() const { return m_ptr; }
|
||||
|
||||
explicit operator bool() const { return !!m_ptr; }
|
||||
bool operator!() const { return !m_ptr; }
|
||||
|
||||
operator T*() const { return m_ptr; }
|
||||
|
||||
private:
|
||||
T* m_ptr { nullptr };
|
||||
};
|
||||
|
||||
// Non-Owning GCPtr
|
||||
template<typename T>
|
||||
using RawGCPtr = GCPtr<T>;
|
||||
|
||||
// Non-Owning NonnullGCPtr
|
||||
template<typename T>
|
||||
using RawNonnullGCPtr = NonnullGCPtr<T>;
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool operator==(GCPtr<T> const& a, GCPtr<U> const& b)
|
||||
{
|
||||
return a.ptr() == b.ptr();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool operator==(GCPtr<T> const& a, NonnullGCPtr<U> const& b)
|
||||
{
|
||||
return a.ptr() == b.ptr();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool operator==(NonnullGCPtr<T> const& a, NonnullGCPtr<U> const& b)
|
||||
{
|
||||
return a.ptr() == b.ptr();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool operator==(NonnullGCPtr<T> const& a, GCPtr<U> const& b)
|
||||
{
|
||||
return a.ptr() == b.ptr();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
struct Traits<JS::GCPtr<T>> : public DefaultTraits<JS::GCPtr<T>> {
|
||||
static unsigned hash(JS::GCPtr<T> const& value)
|
||||
{
|
||||
return Traits<T*>::hash(value.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Traits<JS::NonnullGCPtr<T>> : public DefaultTraits<JS::NonnullGCPtr<T>> {
|
||||
static unsigned hash(JS::NonnullGCPtr<T> const& value)
|
||||
{
|
||||
return Traits<T*>::hash(value.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
HandleImpl::HandleImpl(CellImpl* cell, SourceLocation location)
|
||||
: m_cell(cell)
|
||||
, m_location(location)
|
||||
{
|
||||
m_cell->heap().did_create_handle({}, *this);
|
||||
}
|
||||
|
||||
HandleImpl::~HandleImpl()
|
||||
{
|
||||
m_cell->heap().did_destroy_handle({}, *this);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/SourceLocation.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class HandleImpl : public RefCounted<HandleImpl> {
|
||||
AK_MAKE_NONCOPYABLE(HandleImpl);
|
||||
AK_MAKE_NONMOVABLE(HandleImpl);
|
||||
|
||||
public:
|
||||
~HandleImpl();
|
||||
|
||||
CellImpl* cell() { return m_cell; }
|
||||
CellImpl const* cell() const { return m_cell; }
|
||||
|
||||
SourceLocation const& source_location() const { return m_location; }
|
||||
|
||||
private:
|
||||
template<class T>
|
||||
friend class Handle;
|
||||
|
||||
explicit HandleImpl(CellImpl*, SourceLocation location);
|
||||
GCPtr<CellImpl> m_cell;
|
||||
SourceLocation m_location;
|
||||
|
||||
IntrusiveListNode<HandleImpl> m_list_node;
|
||||
|
||||
public:
|
||||
using List = IntrusiveList<&HandleImpl::m_list_node>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class Handle {
|
||||
public:
|
||||
Handle() = default;
|
||||
|
||||
static Handle create(T* cell, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
return Handle(adopt_ref(*new HandleImpl(const_cast<RemoveConst<T>*>(cell), location)));
|
||||
}
|
||||
|
||||
Handle(T* cell, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
if (cell)
|
||||
m_impl = adopt_ref(*new HandleImpl(cell, location));
|
||||
}
|
||||
|
||||
Handle(T& cell, SourceLocation location = SourceLocation::current())
|
||||
: m_impl(adopt_ref(*new HandleImpl(&cell, location)))
|
||||
{
|
||||
}
|
||||
|
||||
Handle(GCPtr<T> cell, SourceLocation location = SourceLocation::current())
|
||||
: Handle(cell.ptr(), location)
|
||||
{
|
||||
}
|
||||
|
||||
Handle(NonnullGCPtr<T> cell, SourceLocation location = SourceLocation::current())
|
||||
: Handle(*cell, location)
|
||||
{
|
||||
}
|
||||
|
||||
T* cell() const
|
||||
{
|
||||
if (!m_impl)
|
||||
return nullptr;
|
||||
return static_cast<T*>(m_impl->cell());
|
||||
}
|
||||
|
||||
T* ptr() const
|
||||
{
|
||||
return cell();
|
||||
}
|
||||
|
||||
bool is_null() const
|
||||
{
|
||||
return m_impl.is_null();
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return cell();
|
||||
}
|
||||
|
||||
[[nodiscard]] T& operator*() const
|
||||
{
|
||||
return *cell();
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return !cell();
|
||||
}
|
||||
operator bool() const
|
||||
{
|
||||
return cell();
|
||||
}
|
||||
|
||||
operator T*() const { return cell(); }
|
||||
|
||||
private:
|
||||
explicit Handle(NonnullRefPtr<HandleImpl> impl)
|
||||
: m_impl(move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<HandleImpl> m_impl;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline Handle<T> make_handle(T* cell, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
if (!cell)
|
||||
return Handle<T> {};
|
||||
return Handle<T>::create(cell, location);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline Handle<T> make_handle(T& cell, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
return Handle<T>::create(&cell, location);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline Handle<T> make_handle(GCPtr<T> cell, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
if (!cell)
|
||||
return Handle<T> {};
|
||||
return Handle<T>::create(cell.ptr(), location);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline Handle<T> make_handle(NonnullGCPtr<T> cell, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
return Handle<T>::create(cell.ptr(), location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
struct Traits<JS::Handle<T>> : public DefaultTraits<JS::Handle<T>> {
|
||||
static unsigned hash(JS::Handle<T> const& handle) { return Traits<T>::hash(handle); }
|
||||
};
|
||||
|
||||
namespace Detail {
|
||||
template<typename T>
|
||||
inline constexpr bool IsHashCompatible<JS::Handle<T>, T> = true;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool IsHashCompatible<T, JS::Handle<T>> = true;
|
||||
|
||||
}
|
||||
}
|
|
@ -1,538 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StackInfo.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibCore/ElapsedTimer.h>
|
||||
#include <LibJS/Heap/CellAllocator.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Heap/HeapBlock.h>
|
||||
#include <LibJS/Heap/NanBoxedValue.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef HAS_ADDRESS_SANITIZER
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
namespace JS {
|
||||
|
||||
Heap::Heap(void* private_data, Function<void(HashMap<CellImpl*, JS::HeapRoot>&)> gather_embedder_roots)
|
||||
: HeapBase(private_data)
|
||||
, m_gather_embedder_roots(move(gather_embedder_roots))
|
||||
{
|
||||
static_assert(HeapBlock::min_possible_cell_size <= 32, "Heap Cell tracking uses too much data!");
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(64));
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(96));
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(128));
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(256));
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(512));
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(1024));
|
||||
m_size_based_cell_allocators.append(make<CellAllocator>(3072));
|
||||
}
|
||||
|
||||
Heap::~Heap()
|
||||
{
|
||||
collect_garbage(CollectionType::CollectEverything);
|
||||
}
|
||||
|
||||
void Heap::will_allocate(size_t size)
|
||||
{
|
||||
if (should_collect_on_every_allocation()) {
|
||||
m_allocated_bytes_since_last_gc = 0;
|
||||
collect_garbage();
|
||||
} else if (m_allocated_bytes_since_last_gc + size > m_gc_bytes_threshold) {
|
||||
m_allocated_bytes_since_last_gc = 0;
|
||||
collect_garbage();
|
||||
}
|
||||
|
||||
m_allocated_bytes_since_last_gc += size;
|
||||
}
|
||||
|
||||
static void add_possible_value(HashMap<FlatPtr, HeapRoot>& possible_pointers, FlatPtr data, HeapRoot origin, FlatPtr min_block_address, FlatPtr max_block_address)
|
||||
{
|
||||
if constexpr (sizeof(FlatPtr*) == sizeof(NanBoxedValue)) {
|
||||
// Because NanBoxedValue stores pointers in non-canonical form we have to check if the top bytes
|
||||
// match any pointer-backed tag, in that case we have to extract the pointer to its
|
||||
// canonical form and add that as a possible pointer.
|
||||
FlatPtr possible_pointer;
|
||||
if ((data & SHIFTED_IS_CELL_PATTERN) == SHIFTED_IS_CELL_PATTERN)
|
||||
possible_pointer = NanBoxedValue::extract_pointer_bits(data);
|
||||
else
|
||||
possible_pointer = data;
|
||||
if (possible_pointer < min_block_address || possible_pointer > max_block_address)
|
||||
return;
|
||||
possible_pointers.set(possible_pointer, move(origin));
|
||||
} else {
|
||||
static_assert((sizeof(NanBoxedValue) % sizeof(FlatPtr*)) == 0);
|
||||
if (data < min_block_address || data > max_block_address)
|
||||
return;
|
||||
// In the 32-bit case we will look at the top and bottom part of NanBoxedValue separately we just
|
||||
// add both the upper and lower bytes as possible pointers.
|
||||
possible_pointers.set(data, move(origin));
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::find_min_and_max_block_addresses(FlatPtr& min_address, FlatPtr& max_address)
|
||||
{
|
||||
min_address = explode_byte(0xff);
|
||||
max_address = 0;
|
||||
for (auto& allocator : m_all_cell_allocators) {
|
||||
min_address = min(min_address, allocator.min_block_address());
|
||||
max_address = max(max_address, allocator.max_block_address() + HeapBlockBase::block_size);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
static void for_each_cell_among_possible_pointers(HashTable<HeapBlock*> const& all_live_heap_blocks, HashMap<FlatPtr, HeapRoot>& possible_pointers, Callback callback)
|
||||
{
|
||||
for (auto possible_pointer : possible_pointers.keys()) {
|
||||
if (!possible_pointer)
|
||||
continue;
|
||||
auto* possible_heap_block = HeapBlock::from_cell(reinterpret_cast<CellImpl const*>(possible_pointer));
|
||||
if (!all_live_heap_blocks.contains(possible_heap_block))
|
||||
continue;
|
||||
if (auto* cell = possible_heap_block->cell_from_possible_pointer(possible_pointer)) {
|
||||
callback(cell, possible_pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GraphConstructorVisitor final : public CellImpl::Visitor {
|
||||
public:
|
||||
explicit GraphConstructorVisitor(Heap& heap, HashMap<CellImpl*, HeapRoot> const& roots)
|
||||
: m_heap(heap)
|
||||
{
|
||||
m_heap.find_min_and_max_block_addresses(m_min_block_address, m_max_block_address);
|
||||
m_heap.for_each_block([&](auto& block) {
|
||||
m_all_live_heap_blocks.set(&block);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
for (auto& [root, root_origin] : roots) {
|
||||
auto& graph_node = m_graph.ensure(bit_cast<FlatPtr>(root));
|
||||
graph_node.class_name = root->class_name();
|
||||
graph_node.root_origin = root_origin;
|
||||
|
||||
m_work_queue.append(*root);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit_impl(CellImpl& cell) override
|
||||
{
|
||||
if (m_node_being_visited)
|
||||
m_node_being_visited->edges.set(reinterpret_cast<FlatPtr>(&cell));
|
||||
|
||||
if (m_graph.get(reinterpret_cast<FlatPtr>(&cell)).has_value())
|
||||
return;
|
||||
|
||||
m_work_queue.append(cell);
|
||||
}
|
||||
|
||||
virtual void visit_possible_values(ReadonlyBytes bytes) override
|
||||
{
|
||||
HashMap<FlatPtr, HeapRoot> possible_pointers;
|
||||
|
||||
auto* raw_pointer_sized_values = reinterpret_cast<FlatPtr const*>(bytes.data());
|
||||
for (size_t i = 0; i < (bytes.size() / sizeof(FlatPtr)); ++i)
|
||||
add_possible_value(possible_pointers, raw_pointer_sized_values[i], HeapRoot { .type = HeapRoot::Type::HeapFunctionCapturedPointer }, m_min_block_address, m_max_block_address);
|
||||
|
||||
for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](CellImpl* cell, FlatPtr) {
|
||||
if (m_node_being_visited)
|
||||
m_node_being_visited->edges.set(reinterpret_cast<FlatPtr>(cell));
|
||||
|
||||
if (m_graph.get(reinterpret_cast<FlatPtr>(&cell)).has_value())
|
||||
return;
|
||||
m_work_queue.append(*cell);
|
||||
});
|
||||
}
|
||||
|
||||
void visit_all_cells()
|
||||
{
|
||||
while (!m_work_queue.is_empty()) {
|
||||
auto cell = m_work_queue.take_last();
|
||||
m_node_being_visited = &m_graph.ensure(bit_cast<FlatPtr>(cell.ptr()));
|
||||
m_node_being_visited->class_name = cell->class_name();
|
||||
cell->visit_edges(*this);
|
||||
m_node_being_visited = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AK::JsonObject dump()
|
||||
{
|
||||
auto graph = AK::JsonObject();
|
||||
for (auto& it : m_graph) {
|
||||
AK::JsonArray edges;
|
||||
for (auto const& value : it.value.edges) {
|
||||
edges.must_append(ByteString::formatted("{}", value));
|
||||
}
|
||||
|
||||
auto node = AK::JsonObject();
|
||||
if (it.value.root_origin.has_value()) {
|
||||
auto type = it.value.root_origin->type;
|
||||
auto location = it.value.root_origin->location;
|
||||
switch (type) {
|
||||
case HeapRoot::Type::Handle:
|
||||
node.set("root"sv, ByteString::formatted("Handle {} {}:{}", location->function_name(), location->filename(), location->line_number()));
|
||||
break;
|
||||
case HeapRoot::Type::MarkedVector:
|
||||
node.set("root"sv, "MarkedVector");
|
||||
break;
|
||||
case HeapRoot::Type::RegisterPointer:
|
||||
node.set("root"sv, "RegisterPointer");
|
||||
break;
|
||||
case HeapRoot::Type::StackPointer:
|
||||
node.set("root"sv, "StackPointer");
|
||||
break;
|
||||
case HeapRoot::Type::VM:
|
||||
node.set("root"sv, "VM");
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
node.set("class_name"sv, it.value.class_name);
|
||||
node.set("edges"sv, edges);
|
||||
graph.set(ByteString::number(it.key), node);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
private:
|
||||
struct GraphNode {
|
||||
Optional<HeapRoot> root_origin;
|
||||
StringView class_name;
|
||||
HashTable<FlatPtr> edges {};
|
||||
};
|
||||
|
||||
GraphNode* m_node_being_visited { nullptr };
|
||||
Vector<NonnullGCPtr<CellImpl>> m_work_queue;
|
||||
HashMap<FlatPtr, GraphNode> m_graph;
|
||||
|
||||
Heap& m_heap;
|
||||
HashTable<HeapBlock*> m_all_live_heap_blocks;
|
||||
FlatPtr m_min_block_address;
|
||||
FlatPtr m_max_block_address;
|
||||
};
|
||||
|
||||
AK::JsonObject Heap::dump_graph()
|
||||
{
|
||||
HashMap<CellImpl*, HeapRoot> roots;
|
||||
gather_roots(roots);
|
||||
GraphConstructorVisitor visitor(*this, roots);
|
||||
visitor.visit_all_cells();
|
||||
return visitor.dump();
|
||||
}
|
||||
|
||||
void Heap::collect_garbage(CollectionType collection_type, bool print_report)
|
||||
{
|
||||
VERIFY(!m_collecting_garbage);
|
||||
TemporaryChange change(m_collecting_garbage, true);
|
||||
|
||||
Core::ElapsedTimer collection_measurement_timer;
|
||||
if (print_report)
|
||||
collection_measurement_timer.start();
|
||||
|
||||
if (collection_type == CollectionType::CollectGarbage) {
|
||||
if (m_gc_deferrals) {
|
||||
m_should_gc_when_deferral_ends = true;
|
||||
return;
|
||||
}
|
||||
HashMap<CellImpl*, HeapRoot> roots;
|
||||
gather_roots(roots);
|
||||
mark_live_cells(roots);
|
||||
}
|
||||
finalize_unmarked_cells();
|
||||
sweep_dead_cells(print_report, collection_measurement_timer);
|
||||
}
|
||||
|
||||
void Heap::gather_roots(HashMap<CellImpl*, HeapRoot>& roots)
|
||||
{
|
||||
m_gather_embedder_roots(roots);
|
||||
gather_conservative_roots(roots);
|
||||
|
||||
for (auto& handle : m_handles)
|
||||
roots.set(handle.cell(), HeapRoot { .type = HeapRoot::Type::Handle, .location = &handle.source_location() });
|
||||
|
||||
for (auto& vector : m_marked_vectors)
|
||||
vector.gather_roots(roots);
|
||||
|
||||
if constexpr (HEAP_DEBUG) {
|
||||
dbgln("gather_roots:");
|
||||
for (auto* root : roots.keys())
|
||||
dbgln(" + {}", root);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_ADDRESS_SANITIZER
|
||||
NO_SANITIZE_ADDRESS void Heap::gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>& possible_pointers, FlatPtr addr, FlatPtr min_block_address, FlatPtr max_block_address)
|
||||
{
|
||||
void* begin = nullptr;
|
||||
void* end = nullptr;
|
||||
void* real_stack = __asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), reinterpret_cast<void*>(addr), &begin, &end);
|
||||
|
||||
if (real_stack != nullptr) {
|
||||
for (auto* real_stack_addr = reinterpret_cast<void const* const*>(begin); real_stack_addr < end; ++real_stack_addr) {
|
||||
void const* real_address = *real_stack_addr;
|
||||
if (real_address == nullptr)
|
||||
continue;
|
||||
add_possible_value(possible_pointers, reinterpret_cast<FlatPtr>(real_address), HeapRoot { .type = HeapRoot::Type::StackPointer }, min_block_address, max_block_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Heap::gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>&, FlatPtr, FlatPtr, FlatPtr)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
NO_SANITIZE_ADDRESS void Heap::gather_conservative_roots(HashMap<CellImpl*, HeapRoot>& roots)
|
||||
{
|
||||
FlatPtr dummy;
|
||||
|
||||
dbgln_if(HEAP_DEBUG, "gather_conservative_roots:");
|
||||
|
||||
jmp_buf buf;
|
||||
setjmp(buf);
|
||||
|
||||
HashMap<FlatPtr, HeapRoot> possible_pointers;
|
||||
|
||||
auto* raw_jmp_buf = reinterpret_cast<FlatPtr const*>(buf);
|
||||
|
||||
FlatPtr min_block_address, max_block_address;
|
||||
find_min_and_max_block_addresses(min_block_address, max_block_address);
|
||||
|
||||
for (size_t i = 0; i < ((size_t)sizeof(buf)) / sizeof(FlatPtr); ++i)
|
||||
add_possible_value(possible_pointers, raw_jmp_buf[i], HeapRoot { .type = HeapRoot::Type::RegisterPointer }, min_block_address, max_block_address);
|
||||
|
||||
auto stack_reference = bit_cast<FlatPtr>(&dummy);
|
||||
|
||||
for (FlatPtr stack_address = stack_reference; stack_address < m_stack_info.top(); stack_address += sizeof(FlatPtr)) {
|
||||
auto data = *reinterpret_cast<FlatPtr*>(stack_address);
|
||||
add_possible_value(possible_pointers, data, HeapRoot { .type = HeapRoot::Type::StackPointer }, min_block_address, max_block_address);
|
||||
gather_asan_fake_stack_roots(possible_pointers, data, min_block_address, max_block_address);
|
||||
}
|
||||
|
||||
for (auto& vector : m_conservative_vectors) {
|
||||
for (auto possible_value : vector.possible_values()) {
|
||||
add_possible_value(possible_pointers, possible_value, HeapRoot { .type = HeapRoot::Type::ConservativeVector }, min_block_address, max_block_address);
|
||||
}
|
||||
}
|
||||
|
||||
HashTable<HeapBlock*> all_live_heap_blocks;
|
||||
for_each_block([&](auto& block) {
|
||||
all_live_heap_blocks.set(&block);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
for_each_cell_among_possible_pointers(all_live_heap_blocks, possible_pointers, [&](CellImpl* cell, FlatPtr possible_pointer) {
|
||||
if (cell->state() == CellImpl::State::Live) {
|
||||
dbgln_if(HEAP_DEBUG, " ?-> {}", (void const*)cell);
|
||||
roots.set(cell, *possible_pointers.get(possible_pointer));
|
||||
} else {
|
||||
dbgln_if(HEAP_DEBUG, " #-> {}", (void const*)cell);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class MarkingVisitor final : public CellImpl::Visitor {
|
||||
public:
|
||||
explicit MarkingVisitor(Heap& heap, HashMap<CellImpl*, HeapRoot> const& roots)
|
||||
: m_heap(heap)
|
||||
{
|
||||
m_heap.find_min_and_max_block_addresses(m_min_block_address, m_max_block_address);
|
||||
m_heap.for_each_block([&](auto& block) {
|
||||
m_all_live_heap_blocks.set(&block);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
for (auto* root : roots.keys()) {
|
||||
visit(root);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit_impl(CellImpl& cell) override
|
||||
{
|
||||
if (cell.is_marked())
|
||||
return;
|
||||
dbgln_if(HEAP_DEBUG, " ! {}", &cell);
|
||||
|
||||
cell.set_marked(true);
|
||||
m_work_queue.append(cell);
|
||||
}
|
||||
|
||||
virtual void visit_possible_values(ReadonlyBytes bytes) override
|
||||
{
|
||||
HashMap<FlatPtr, HeapRoot> possible_pointers;
|
||||
|
||||
auto* raw_pointer_sized_values = reinterpret_cast<FlatPtr const*>(bytes.data());
|
||||
for (size_t i = 0; i < (bytes.size() / sizeof(FlatPtr)); ++i)
|
||||
add_possible_value(possible_pointers, raw_pointer_sized_values[i], HeapRoot { .type = HeapRoot::Type::HeapFunctionCapturedPointer }, m_min_block_address, m_max_block_address);
|
||||
|
||||
for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](CellImpl* cell, FlatPtr) {
|
||||
if (cell->is_marked())
|
||||
return;
|
||||
if (cell->state() != CellImpl::State::Live)
|
||||
return;
|
||||
cell->set_marked(true);
|
||||
m_work_queue.append(*cell);
|
||||
});
|
||||
}
|
||||
|
||||
void mark_all_live_cells()
|
||||
{
|
||||
while (!m_work_queue.is_empty()) {
|
||||
m_work_queue.take_last()->visit_edges(*this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Heap& m_heap;
|
||||
Vector<NonnullGCPtr<CellImpl>> m_work_queue;
|
||||
HashTable<HeapBlock*> m_all_live_heap_blocks;
|
||||
FlatPtr m_min_block_address;
|
||||
FlatPtr m_max_block_address;
|
||||
};
|
||||
|
||||
void Heap::mark_live_cells(HashMap<CellImpl*, HeapRoot> const& roots)
|
||||
{
|
||||
dbgln_if(HEAP_DEBUG, "mark_live_cells:");
|
||||
|
||||
MarkingVisitor visitor(*this, roots);
|
||||
|
||||
visitor.mark_all_live_cells();
|
||||
|
||||
for (auto& inverse_root : m_uprooted_cells)
|
||||
inverse_root->set_marked(false);
|
||||
|
||||
m_uprooted_cells.clear();
|
||||
}
|
||||
|
||||
bool Heap::cell_must_survive_garbage_collection(CellImpl const& cell)
|
||||
{
|
||||
if (!cell.overrides_must_survive_garbage_collection({}))
|
||||
return false;
|
||||
return cell.must_survive_garbage_collection();
|
||||
}
|
||||
|
||||
void Heap::finalize_unmarked_cells()
|
||||
{
|
||||
for_each_block([&](auto& block) {
|
||||
block.template for_each_cell_in_state<CellImpl::State::Live>([](CellImpl* cell) {
|
||||
if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell))
|
||||
cell->finalize();
|
||||
});
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measurement_timer)
|
||||
{
|
||||
dbgln_if(HEAP_DEBUG, "sweep_dead_cells:");
|
||||
Vector<HeapBlock*, 32> empty_blocks;
|
||||
Vector<HeapBlock*, 32> full_blocks_that_became_usable;
|
||||
|
||||
size_t collected_cells = 0;
|
||||
size_t live_cells = 0;
|
||||
size_t collected_cell_bytes = 0;
|
||||
size_t live_cell_bytes = 0;
|
||||
|
||||
for_each_block([&](auto& block) {
|
||||
bool block_has_live_cells = false;
|
||||
bool block_was_full = block.is_full();
|
||||
block.template for_each_cell_in_state<CellImpl::State::Live>([&](CellImpl* cell) {
|
||||
if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell)) {
|
||||
dbgln_if(HEAP_DEBUG, " ~ {}", cell);
|
||||
block.deallocate(cell);
|
||||
++collected_cells;
|
||||
collected_cell_bytes += block.cell_size();
|
||||
} else {
|
||||
cell->set_marked(false);
|
||||
block_has_live_cells = true;
|
||||
++live_cells;
|
||||
live_cell_bytes += block.cell_size();
|
||||
}
|
||||
});
|
||||
if (!block_has_live_cells)
|
||||
empty_blocks.append(&block);
|
||||
else if (block_was_full != block.is_full())
|
||||
full_blocks_that_became_usable.append(&block);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
for (auto& weak_container : m_weak_containers)
|
||||
weak_container.remove_dead_cells({});
|
||||
|
||||
for (auto* block : empty_blocks) {
|
||||
dbgln_if(HEAP_DEBUG, " - HeapBlock empty @ {}: cell_size={}", block, block->cell_size());
|
||||
block->cell_allocator().block_did_become_empty({}, *block);
|
||||
}
|
||||
|
||||
for (auto* block : full_blocks_that_became_usable) {
|
||||
dbgln_if(HEAP_DEBUG, " - HeapBlock usable again @ {}: cell_size={}", block, block->cell_size());
|
||||
block->cell_allocator().block_did_become_usable({}, *block);
|
||||
}
|
||||
|
||||
if constexpr (HEAP_DEBUG) {
|
||||
for_each_block([&](auto& block) {
|
||||
dbgln(" > Live HeapBlock @ {}: cell_size={}", &block, block.cell_size());
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
m_gc_bytes_threshold = live_cell_bytes > GC_MIN_BYTES_THRESHOLD ? live_cell_bytes : GC_MIN_BYTES_THRESHOLD;
|
||||
|
||||
if (print_report) {
|
||||
AK::Duration const time_spent = measurement_timer.elapsed_time();
|
||||
size_t live_block_count = 0;
|
||||
for_each_block([&](auto&) {
|
||||
++live_block_count;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
dbgln("Garbage collection report");
|
||||
dbgln("=============================================");
|
||||
dbgln(" Time spent: {} ms", time_spent.to_milliseconds());
|
||||
dbgln(" Live cells: {} ({} bytes)", live_cells, live_cell_bytes);
|
||||
dbgln("Collected cells: {} ({} bytes)", collected_cells, collected_cell_bytes);
|
||||
dbgln(" Live blocks: {} ({} bytes)", live_block_count, live_block_count * HeapBlock::block_size);
|
||||
dbgln(" Freed blocks: {} ({} bytes)", empty_blocks.size(), empty_blocks.size() * HeapBlock::block_size);
|
||||
dbgln("=============================================");
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::defer_gc()
|
||||
{
|
||||
++m_gc_deferrals;
|
||||
}
|
||||
|
||||
void Heap::undefer_gc()
|
||||
{
|
||||
VERIFY(m_gc_deferrals > 0);
|
||||
--m_gc_deferrals;
|
||||
|
||||
if (!m_gc_deferrals) {
|
||||
if (m_should_gc_when_deferral_ends)
|
||||
collect_garbage();
|
||||
m_should_gc_when_deferral_ends = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::uproot_cell(CellImpl* cell)
|
||||
{
|
||||
m_uprooted_cells.append(cell);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/StackInfo.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/CellAllocator.h>
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/ConservativeVector.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibJS/Heap/HeapRoot.h>
|
||||
#include <LibJS/Heap/Internals.h>
|
||||
#include <LibJS/Heap/MarkedVector.h>
|
||||
#include <LibJS/Heap/WeakContainer.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class Heap : public HeapBase {
|
||||
AK_MAKE_NONCOPYABLE(Heap);
|
||||
AK_MAKE_NONMOVABLE(Heap);
|
||||
|
||||
public:
|
||||
explicit Heap(void* private_data, Function<void(HashMap<CellImpl*, JS::HeapRoot>&)> gather_embedder_roots);
|
||||
~Heap();
|
||||
|
||||
template<typename T, typename... Args>
|
||||
NonnullGCPtr<T> allocate(Args&&... args)
|
||||
{
|
||||
auto* memory = allocate_cell<T>();
|
||||
defer_gc();
|
||||
new (memory) T(forward<Args>(args)...);
|
||||
undefer_gc();
|
||||
return *static_cast<T*>(memory);
|
||||
}
|
||||
|
||||
enum class CollectionType {
|
||||
CollectGarbage,
|
||||
CollectEverything,
|
||||
};
|
||||
|
||||
void collect_garbage(CollectionType = CollectionType::CollectGarbage, bool print_report = false);
|
||||
AK::JsonObject dump_graph();
|
||||
|
||||
bool should_collect_on_every_allocation() const { return m_should_collect_on_every_allocation; }
|
||||
void set_should_collect_on_every_allocation(bool b) { m_should_collect_on_every_allocation = b; }
|
||||
|
||||
void did_create_handle(Badge<HandleImpl>, HandleImpl&);
|
||||
void did_destroy_handle(Badge<HandleImpl>, HandleImpl&);
|
||||
|
||||
void did_create_marked_vector(Badge<MarkedVectorBase>, MarkedVectorBase&);
|
||||
void did_destroy_marked_vector(Badge<MarkedVectorBase>, MarkedVectorBase&);
|
||||
|
||||
void did_create_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase&);
|
||||
void did_destroy_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase&);
|
||||
|
||||
void did_create_weak_container(Badge<WeakContainer>, WeakContainer&);
|
||||
void did_destroy_weak_container(Badge<WeakContainer>, WeakContainer&);
|
||||
|
||||
void register_cell_allocator(Badge<CellAllocator>, CellAllocator&);
|
||||
|
||||
void uproot_cell(CellImpl* cell);
|
||||
|
||||
private:
|
||||
friend class MarkingVisitor;
|
||||
friend class GraphConstructorVisitor;
|
||||
friend class DeferGC;
|
||||
|
||||
void defer_gc();
|
||||
void undefer_gc();
|
||||
|
||||
static bool cell_must_survive_garbage_collection(CellImpl const&);
|
||||
|
||||
template<typename T>
|
||||
CellImpl* allocate_cell()
|
||||
{
|
||||
will_allocate(sizeof(T));
|
||||
if constexpr (requires { T::cell_allocator.allocator.get().allocate_cell(*this); }) {
|
||||
if constexpr (IsSame<T, typename decltype(T::cell_allocator)::CellType>) {
|
||||
return T::cell_allocator.allocator.get().allocate_cell(*this);
|
||||
}
|
||||
}
|
||||
return allocator_for_size(sizeof(T)).allocate_cell(*this);
|
||||
}
|
||||
|
||||
void will_allocate(size_t);
|
||||
|
||||
void find_min_and_max_block_addresses(FlatPtr& min_address, FlatPtr& max_address);
|
||||
void gather_roots(HashMap<CellImpl*, HeapRoot>&);
|
||||
void gather_conservative_roots(HashMap<CellImpl*, HeapRoot>&);
|
||||
void gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>&, FlatPtr, FlatPtr min_block_address, FlatPtr max_block_address);
|
||||
void mark_live_cells(HashMap<CellImpl*, HeapRoot> const& live_cells);
|
||||
void finalize_unmarked_cells();
|
||||
void sweep_dead_cells(bool print_report, Core::ElapsedTimer const&);
|
||||
|
||||
ALWAYS_INLINE CellAllocator& allocator_for_size(size_t cell_size)
|
||||
{
|
||||
// FIXME: Use binary search?
|
||||
for (auto& allocator : m_size_based_cell_allocators) {
|
||||
if (allocator->cell_size() >= cell_size)
|
||||
return *allocator;
|
||||
}
|
||||
dbgln("Cannot get CellAllocator for cell size {}, largest available is {}!", cell_size, m_size_based_cell_allocators.last()->cell_size());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_block(Callback callback)
|
||||
{
|
||||
for (auto& allocator : m_all_cell_allocators) {
|
||||
if (allocator.for_each_block(callback) == IterationDecision::Break)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t GC_MIN_BYTES_THRESHOLD { 4 * 1024 * 1024 };
|
||||
size_t m_gc_bytes_threshold { GC_MIN_BYTES_THRESHOLD };
|
||||
size_t m_allocated_bytes_since_last_gc { 0 };
|
||||
|
||||
bool m_should_collect_on_every_allocation { false };
|
||||
|
||||
Vector<NonnullOwnPtr<CellAllocator>> m_size_based_cell_allocators;
|
||||
CellAllocator::List m_all_cell_allocators;
|
||||
|
||||
HandleImpl::List m_handles;
|
||||
MarkedVectorBase::List m_marked_vectors;
|
||||
ConservativeVectorBase::List m_conservative_vectors;
|
||||
WeakContainer::List m_weak_containers;
|
||||
|
||||
Vector<GCPtr<CellImpl>> m_uprooted_cells;
|
||||
|
||||
size_t m_gc_deferrals { 0 };
|
||||
bool m_should_gc_when_deferral_ends { false };
|
||||
|
||||
bool m_collecting_garbage { false };
|
||||
StackInfo m_stack_info;
|
||||
Function<void(HashMap<CellImpl*, JS::HeapRoot>&)> m_gather_embedder_roots;
|
||||
};
|
||||
|
||||
inline void Heap::did_create_handle(Badge<HandleImpl>, HandleImpl& impl)
|
||||
{
|
||||
VERIFY(!m_handles.contains(impl));
|
||||
m_handles.append(impl);
|
||||
}
|
||||
|
||||
inline void Heap::did_destroy_handle(Badge<HandleImpl>, HandleImpl& impl)
|
||||
{
|
||||
VERIFY(m_handles.contains(impl));
|
||||
m_handles.remove(impl);
|
||||
}
|
||||
|
||||
inline void Heap::did_create_marked_vector(Badge<MarkedVectorBase>, MarkedVectorBase& vector)
|
||||
{
|
||||
VERIFY(!m_marked_vectors.contains(vector));
|
||||
m_marked_vectors.append(vector);
|
||||
}
|
||||
|
||||
inline void Heap::did_destroy_marked_vector(Badge<MarkedVectorBase>, MarkedVectorBase& vector)
|
||||
{
|
||||
VERIFY(m_marked_vectors.contains(vector));
|
||||
m_marked_vectors.remove(vector);
|
||||
}
|
||||
|
||||
inline void Heap::did_create_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase& vector)
|
||||
{
|
||||
VERIFY(!m_conservative_vectors.contains(vector));
|
||||
m_conservative_vectors.append(vector);
|
||||
}
|
||||
|
||||
inline void Heap::did_destroy_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase& vector)
|
||||
{
|
||||
VERIFY(m_conservative_vectors.contains(vector));
|
||||
m_conservative_vectors.remove(vector);
|
||||
}
|
||||
|
||||
inline void Heap::did_create_weak_container(Badge<WeakContainer>, WeakContainer& set)
|
||||
{
|
||||
VERIFY(!m_weak_containers.contains(set));
|
||||
m_weak_containers.append(set);
|
||||
}
|
||||
|
||||
inline void Heap::did_destroy_weak_container(Badge<WeakContainer>, WeakContainer& set)
|
||||
{
|
||||
VERIFY(m_weak_containers.contains(set));
|
||||
m_weak_containers.remove(set);
|
||||
}
|
||||
|
||||
inline void Heap::register_cell_allocator(Badge<CellAllocator>, CellAllocator& allocator)
|
||||
{
|
||||
m_all_cell_allocators.append(allocator);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Heap/HeapBlock.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifdef HAS_ADDRESS_SANITIZER
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
namespace JS {
|
||||
|
||||
size_t HeapBlockBase::block_size = PAGE_SIZE;
|
||||
|
||||
NonnullOwnPtr<HeapBlock> HeapBlock::create_with_cell_size(Heap& heap, CellAllocator& cell_allocator, size_t cell_size, [[maybe_unused]] char const* class_name)
|
||||
{
|
||||
char const* name = nullptr;
|
||||
auto* block = static_cast<HeapBlock*>(cell_allocator.block_allocator().allocate_block(name));
|
||||
new (block) HeapBlock(heap, cell_allocator, cell_size);
|
||||
return NonnullOwnPtr<HeapBlock>(NonnullOwnPtr<HeapBlock>::Adopt, *block);
|
||||
}
|
||||
|
||||
HeapBlock::HeapBlock(Heap& heap, CellAllocator& cell_allocator, size_t cell_size)
|
||||
: HeapBlockBase(heap)
|
||||
, m_cell_allocator(cell_allocator)
|
||||
, m_cell_size(cell_size)
|
||||
{
|
||||
VERIFY(cell_size >= sizeof(FreelistEntry));
|
||||
ASAN_POISON_MEMORY_REGION(m_storage, block_size - sizeof(HeapBlock));
|
||||
}
|
||||
|
||||
void HeapBlock::deallocate(CellImpl* cell)
|
||||
{
|
||||
VERIFY(is_valid_cell_pointer(cell));
|
||||
VERIFY(!m_freelist || is_valid_cell_pointer(m_freelist));
|
||||
VERIFY(cell->state() == CellImpl::State::Live);
|
||||
VERIFY(!cell->is_marked());
|
||||
|
||||
cell->~CellImpl();
|
||||
auto* freelist_entry = new (cell) FreelistEntry();
|
||||
freelist_entry->set_state(CellImpl::State::Dead);
|
||||
freelist_entry->next = m_freelist;
|
||||
m_freelist = freelist_entry;
|
||||
|
||||
#ifdef HAS_ADDRESS_SANITIZER
|
||||
auto dword_after_freelist = round_up_to_power_of_two(reinterpret_cast<uintptr_t>(freelist_entry) + sizeof(FreelistEntry), 8);
|
||||
VERIFY((dword_after_freelist - reinterpret_cast<uintptr_t>(freelist_entry)) <= m_cell_size);
|
||||
VERIFY(m_cell_size >= sizeof(FreelistEntry));
|
||||
// We can't poision the cell tracking data, nor the FreeListEntry's vtable or next pointer
|
||||
// This means there's sizeof(FreelistEntry) data at the front of each cell that is always read/write
|
||||
// On x86_64, this ends up being 24 bytes due to the size of the FreeListEntry's vtable, while on x86, it's only 12 bytes.
|
||||
ASAN_POISON_MEMORY_REGION(reinterpret_cast<void*>(dword_after_freelist), m_cell_size - sizeof(FreelistEntry));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/Internals.h>
|
||||
|
||||
#ifdef HAS_ADDRESS_SANITIZER
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
namespace JS {
|
||||
|
||||
class HeapBlock : public HeapBlockBase {
|
||||
AK_MAKE_NONCOPYABLE(HeapBlock);
|
||||
AK_MAKE_NONMOVABLE(HeapBlock);
|
||||
|
||||
public:
|
||||
using HeapBlockBase::block_size;
|
||||
static NonnullOwnPtr<HeapBlock> create_with_cell_size(Heap&, CellAllocator&, size_t cell_size, char const* class_name);
|
||||
|
||||
size_t cell_size() const { return m_cell_size; }
|
||||
size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; }
|
||||
bool is_full() const { return !has_lazy_freelist() && !m_freelist; }
|
||||
|
||||
ALWAYS_INLINE CellImpl* allocate()
|
||||
{
|
||||
CellImpl* allocated_cell = nullptr;
|
||||
if (m_freelist) {
|
||||
VERIFY(is_valid_cell_pointer(m_freelist));
|
||||
allocated_cell = exchange(m_freelist, m_freelist->next);
|
||||
} else if (has_lazy_freelist()) {
|
||||
allocated_cell = cell(m_next_lazy_freelist_index++);
|
||||
}
|
||||
|
||||
if (allocated_cell) {
|
||||
ASAN_UNPOISON_MEMORY_REGION(allocated_cell, m_cell_size);
|
||||
}
|
||||
return allocated_cell;
|
||||
}
|
||||
|
||||
void deallocate(CellImpl*);
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_cell(Callback callback)
|
||||
{
|
||||
auto end = has_lazy_freelist() ? m_next_lazy_freelist_index : cell_count();
|
||||
for (size_t i = 0; i < end; ++i)
|
||||
callback(cell(i));
|
||||
}
|
||||
|
||||
template<CellImpl::State state, typename Callback>
|
||||
void for_each_cell_in_state(Callback callback)
|
||||
{
|
||||
for_each_cell([&](auto* cell) {
|
||||
if (cell->state() == state)
|
||||
callback(cell);
|
||||
});
|
||||
}
|
||||
|
||||
static HeapBlock* from_cell(CellImpl const* cell)
|
||||
{
|
||||
return static_cast<HeapBlock*>(HeapBlockBase::from_cell(cell));
|
||||
}
|
||||
|
||||
CellImpl* cell_from_possible_pointer(FlatPtr pointer)
|
||||
{
|
||||
if (pointer < reinterpret_cast<FlatPtr>(m_storage))
|
||||
return nullptr;
|
||||
size_t cell_index = (pointer - reinterpret_cast<FlatPtr>(m_storage)) / m_cell_size;
|
||||
auto end = has_lazy_freelist() ? m_next_lazy_freelist_index : cell_count();
|
||||
if (cell_index >= end)
|
||||
return nullptr;
|
||||
return cell(cell_index);
|
||||
}
|
||||
|
||||
bool is_valid_cell_pointer(CellImpl const* cell)
|
||||
{
|
||||
return cell_from_possible_pointer((FlatPtr)cell);
|
||||
}
|
||||
|
||||
IntrusiveListNode<HeapBlock> m_list_node;
|
||||
|
||||
CellAllocator& cell_allocator() { return m_cell_allocator; }
|
||||
|
||||
private:
|
||||
HeapBlock(Heap&, CellAllocator&, size_t cell_size);
|
||||
|
||||
bool has_lazy_freelist() const { return m_next_lazy_freelist_index < cell_count(); }
|
||||
|
||||
struct FreelistEntry final : public CellImpl {
|
||||
JS_CELL(FreelistEntry, CellImpl);
|
||||
|
||||
RawGCPtr<FreelistEntry> next;
|
||||
};
|
||||
|
||||
CellImpl* cell(size_t index)
|
||||
{
|
||||
return reinterpret_cast<CellImpl*>(&m_storage[index * cell_size()]);
|
||||
}
|
||||
|
||||
CellAllocator& m_cell_allocator;
|
||||
size_t m_cell_size { 0 };
|
||||
size_t m_next_lazy_freelist_index { 0 };
|
||||
GCPtr<FreelistEntry> m_freelist;
|
||||
alignas(__BIGGEST_ALIGNMENT__) u8 m_storage[];
|
||||
|
||||
public:
|
||||
static constexpr size_t min_possible_cell_size = sizeof(FreelistEntry);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
template<typename T>
|
||||
class HeapFunction final : public CellImpl {
|
||||
JS_CELL(HeapFunction, CellImpl);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<HeapFunction> create(Heap& heap, Function<T> function)
|
||||
{
|
||||
return heap.allocate<HeapFunction>(move(function));
|
||||
}
|
||||
|
||||
virtual ~HeapFunction() override = default;
|
||||
|
||||
[[nodiscard]] Function<T> const& function() const { return m_function; }
|
||||
|
||||
private:
|
||||
HeapFunction(Function<T> function)
|
||||
: m_function(move(function))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void visit_edges(Visitor& visitor) override
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit_possible_values(m_function.raw_capture_range());
|
||||
}
|
||||
|
||||
Function<T> m_function;
|
||||
};
|
||||
|
||||
template<typename Callable, typename T = EquivalentFunctionType<Callable>>
|
||||
static NonnullGCPtr<HeapFunction<T>> create_heap_function(Heap& heap, Callable&& function)
|
||||
{
|
||||
return HeapFunction<T>::create(heap, Function<T> { forward<Callable>(function) });
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/SourceLocation.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
struct HeapRoot {
|
||||
enum class Type {
|
||||
HeapFunctionCapturedPointer,
|
||||
Handle,
|
||||
MarkedVector,
|
||||
ConservativeVector,
|
||||
RegisterPointer,
|
||||
StackPointer,
|
||||
VM,
|
||||
};
|
||||
|
||||
Type type;
|
||||
SourceLocation const* location { nullptr };
|
||||
};
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2020-2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class HeapBase {
|
||||
AK_MAKE_NONCOPYABLE(HeapBase);
|
||||
AK_MAKE_NONMOVABLE(HeapBase);
|
||||
|
||||
public:
|
||||
void* private_data() { return m_private_data; }
|
||||
|
||||
protected:
|
||||
explicit HeapBase(void* private_data)
|
||||
: m_private_data(private_data)
|
||||
{
|
||||
}
|
||||
|
||||
void* m_private_data;
|
||||
};
|
||||
|
||||
class HeapBlockBase {
|
||||
AK_MAKE_NONMOVABLE(HeapBlockBase);
|
||||
AK_MAKE_NONCOPYABLE(HeapBlockBase);
|
||||
|
||||
public:
|
||||
static size_t block_size;
|
||||
static HeapBlockBase* from_cell(CellImpl const* cell)
|
||||
{
|
||||
return reinterpret_cast<HeapBlockBase*>(bit_cast<FlatPtr>(cell) & ~(HeapBlockBase::block_size - 1));
|
||||
}
|
||||
|
||||
Heap& heap() { return m_heap; }
|
||||
|
||||
protected:
|
||||
HeapBlockBase(Heap& heap)
|
||||
: m_heap(heap)
|
||||
{
|
||||
}
|
||||
|
||||
Heap& m_heap;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Heap/MarkedVector.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
MarkedVectorBase::MarkedVectorBase(Heap& heap)
|
||||
: m_heap(&heap)
|
||||
{
|
||||
m_heap->did_create_marked_vector({}, *this);
|
||||
}
|
||||
|
||||
MarkedVectorBase::~MarkedVectorBase()
|
||||
{
|
||||
m_heap->did_destroy_marked_vector({}, *this);
|
||||
}
|
||||
|
||||
MarkedVectorBase& MarkedVectorBase::operator=(MarkedVectorBase const& other)
|
||||
{
|
||||
if (m_heap != other.m_heap) {
|
||||
m_heap = other.m_heap;
|
||||
|
||||
// NOTE: IntrusiveList will remove this MarkedVectorBase from the old heap it was part of.
|
||||
m_heap->did_create_marked_vector({}, *this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/CellImpl.h>
|
||||
#include <LibJS/Heap/HeapRoot.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class MarkedVectorBase {
|
||||
public:
|
||||
virtual void gather_roots(HashMap<CellImpl*, JS::HeapRoot>&) const = 0;
|
||||
|
||||
protected:
|
||||
explicit MarkedVectorBase(Heap&);
|
||||
~MarkedVectorBase();
|
||||
|
||||
MarkedVectorBase& operator=(MarkedVectorBase const&);
|
||||
|
||||
Heap* m_heap { nullptr };
|
||||
IntrusiveListNode<MarkedVectorBase> m_list_node;
|
||||
|
||||
public:
|
||||
using List = IntrusiveList<&MarkedVectorBase::m_list_node>;
|
||||
};
|
||||
|
||||
template<typename T, size_t inline_capacity>
|
||||
class MarkedVector final
|
||||
: public MarkedVectorBase
|
||||
, public Vector<T, inline_capacity> {
|
||||
|
||||
public:
|
||||
explicit MarkedVector(Heap& heap)
|
||||
: MarkedVectorBase(heap)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MarkedVector() = default;
|
||||
|
||||
MarkedVector(MarkedVector const& other)
|
||||
: MarkedVectorBase(*other.m_heap)
|
||||
, Vector<T, inline_capacity>(other)
|
||||
{
|
||||
}
|
||||
|
||||
MarkedVector(MarkedVector&& other)
|
||||
: MarkedVectorBase(*other.m_heap)
|
||||
, Vector<T, inline_capacity>(move(static_cast<Vector<T, inline_capacity>&>(other)))
|
||||
{
|
||||
}
|
||||
|
||||
MarkedVector& operator=(MarkedVector const& other)
|
||||
{
|
||||
Vector<T, inline_capacity>::operator=(other);
|
||||
MarkedVectorBase::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void gather_roots(HashMap<CellImpl*, JS::HeapRoot>& roots) const override
|
||||
{
|
||||
for (auto& value : *this) {
|
||||
if constexpr (IsSame<Value, T>) {
|
||||
if (value.is_cell())
|
||||
roots.set(&const_cast<T&>(value).as_cell(), HeapRoot { .type = HeapRoot::Type::MarkedVector });
|
||||
} else {
|
||||
roots.set(value, HeapRoot { .type = HeapRoot::Type::MarkedVector });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BitCast.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
static_assert(sizeof(double) == 8);
|
||||
static_assert(sizeof(void*) == sizeof(double) || sizeof(void*) == sizeof(u32));
|
||||
// To make our Value representation compact we can use the fact that IEEE
|
||||
// doubles have a lot (2^52 - 2) of NaN bit patterns. The canonical form being
|
||||
// just 0x7FF8000000000000 i.e. sign = 0 exponent is all ones and the top most
|
||||
// bit of the mantissa set.
|
||||
static constexpr u64 CANON_NAN_BITS = bit_cast<u64>(__builtin_nan(""));
|
||||
static_assert(CANON_NAN_BITS == 0x7FF8000000000000);
|
||||
// (Unfortunately all the other values are valid so we have to convert any
|
||||
// incoming NaNs to this pattern although in practice it seems only the negative
|
||||
// version of these CANON_NAN_BITS)
|
||||
// +/- Infinity are represented by a full exponent but without any bits of the
|
||||
// mantissa set.
|
||||
static constexpr u64 POSITIVE_INFINITY_BITS = bit_cast<u64>(__builtin_huge_val());
|
||||
static constexpr u64 NEGATIVE_INFINITY_BITS = bit_cast<u64>(-__builtin_huge_val());
|
||||
static_assert(POSITIVE_INFINITY_BITS == 0x7FF0000000000000);
|
||||
static_assert(NEGATIVE_INFINITY_BITS == 0xFFF0000000000000);
|
||||
// However as long as any bit is set in the mantissa with the exponent of all
|
||||
// ones this value is a NaN, and it even ignores the sign bit.
|
||||
// (NOTE: we have to use __builtin_isnan here since some isnan implementations are not constexpr)
|
||||
static_assert(__builtin_isnan(bit_cast<double>(0x7FF0000000000001)));
|
||||
static_assert(__builtin_isnan(bit_cast<double>(0xFFF0000000040000)));
|
||||
// This means we can use all of these NaNs to store all other options for Value.
|
||||
// To make sure all of these other representations we use 0x7FF8 as the base top
|
||||
// 2 bytes which ensures the value is always a NaN.
|
||||
static constexpr u64 BASE_TAG = 0x7FF8;
|
||||
// This leaves the sign bit and the three lower bits for tagging a value and then
|
||||
// 48 bits of potential payload.
|
||||
// First the pointer backed types (Object, String etc.), to signify this category
|
||||
// and make stack scanning easier we use the sign bit (top most bit) of 1 to
|
||||
// signify that it is a pointer backed type.
|
||||
static constexpr u64 IS_CELL_BIT = 0x8000 | BASE_TAG;
|
||||
// On all current 64-bit systems this code runs pointer actually only use the
|
||||
// lowest 6 bytes which fits neatly into our NaN payload with the top two bytes
|
||||
// left over for marking it as a NaN and tagging the type.
|
||||
// Note that we do need to take care when extracting the pointer value but this
|
||||
// is explained in the extract_pointer method.
|
||||
|
||||
static constexpr u64 IS_CELL_PATTERN = 0xFFF8ULL;
|
||||
static constexpr u64 TAG_SHIFT = 48;
|
||||
static constexpr u64 TAG_EXTRACTION = 0xFFFF000000000000;
|
||||
static constexpr u64 SHIFTED_IS_CELL_PATTERN = IS_CELL_PATTERN << TAG_SHIFT;
|
||||
|
||||
class NanBoxedValue {
|
||||
public:
|
||||
bool is_cell() const { return (m_value.tag & IS_CELL_PATTERN) == IS_CELL_PATTERN; }
|
||||
|
||||
static constexpr FlatPtr extract_pointer_bits(u64 encoded)
|
||||
{
|
||||
#ifdef AK_ARCH_32_BIT
|
||||
// For 32-bit system the pointer fully fits so we can just return it directly.
|
||||
static_assert(sizeof(void*) == sizeof(u32));
|
||||
return static_cast<FlatPtr>(encoded & 0xffff'ffff);
|
||||
#elif ARCH(X86_64) || ARCH(RISCV64)
|
||||
// For x86_64 and riscv64 the top 16 bits should be sign extending the "real" top bit (47th).
|
||||
// So first shift the top 16 bits away then using the right shift it sign extends the top 16 bits.
|
||||
return static_cast<FlatPtr>((static_cast<i64>(encoded << 16)) >> 16);
|
||||
#elif ARCH(AARCH64) || ARCH(PPC64) || ARCH(PPC64LE)
|
||||
// For AArch64 the top 16 bits of the pointer should be zero.
|
||||
// For PPC64: all 64 bits can be used for pointers, however on Linux only
|
||||
// the lower 43 bits are used for user-space addresses, so
|
||||
// masking off the top 16 bits should match the rest of LibJS.
|
||||
return static_cast<FlatPtr>(encoded & 0xffff'ffff'ffffULL);
|
||||
#else
|
||||
# error "Unknown architecture. Don't know whether pointers need to be sign-extended."
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename PointerType>
|
||||
PointerType* extract_pointer() const
|
||||
{
|
||||
VERIFY(is_cell());
|
||||
return reinterpret_cast<PointerType*>(extract_pointer_bits(m_value.encoded));
|
||||
}
|
||||
|
||||
CellImpl& as_cell()
|
||||
{
|
||||
VERIFY(is_cell());
|
||||
return *extract_pointer<CellImpl>();
|
||||
}
|
||||
|
||||
CellImpl& as_cell() const
|
||||
{
|
||||
VERIFY(is_cell());
|
||||
return *extract_pointer<CellImpl>();
|
||||
}
|
||||
|
||||
bool is_nan() const
|
||||
{
|
||||
return m_value.encoded == CANON_NAN_BITS;
|
||||
}
|
||||
|
||||
protected:
|
||||
union {
|
||||
double as_double;
|
||||
struct {
|
||||
u64 payload : 48;
|
||||
u64 tag : 16;
|
||||
};
|
||||
u64 encoded;
|
||||
} m_value { .encoded = 0 };
|
||||
};
|
||||
|
||||
static_assert(sizeof(NanBoxedValue) == sizeof(double));
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Heap/WeakContainer.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
WeakContainer::WeakContainer(Heap& heap)
|
||||
: m_heap(heap)
|
||||
{
|
||||
m_heap.did_create_weak_container({}, *this);
|
||||
}
|
||||
|
||||
WeakContainer::~WeakContainer()
|
||||
{
|
||||
deregister();
|
||||
}
|
||||
|
||||
void WeakContainer::deregister()
|
||||
{
|
||||
if (!m_registered)
|
||||
return;
|
||||
m_heap.did_destroy_weak_container({}, *this);
|
||||
m_registered = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class WeakContainer {
|
||||
public:
|
||||
explicit WeakContainer(Heap&);
|
||||
virtual ~WeakContainer();
|
||||
|
||||
virtual void remove_dead_cells(Badge<Heap>) = 0;
|
||||
|
||||
protected:
|
||||
void deregister();
|
||||
|
||||
private:
|
||||
bool m_registered { true };
|
||||
Heap& m_heap;
|
||||
|
||||
IntrusiveListNode<WeakContainer> m_list_node;
|
||||
|
||||
public:
|
||||
using List = IntrusiveList<&WeakContainer::m_list_node>;
|
||||
};
|
||||
|
||||
}
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(Module);
|
||||
JS_DEFINE_ALLOCATOR(GraphLoadingState);
|
||||
GC_DEFINE_ALLOCATOR(Module);
|
||||
GC_DEFINE_ALLOCATOR(GraphLoadingState);
|
||||
|
||||
Module::Module(Realm& realm, ByteString filename, Script::HostDefined* host_defined)
|
||||
: m_realm(realm)
|
||||
|
@ -68,14 +68,14 @@ ThrowCompletionOr<u32> Module::inner_module_evaluation(VM& vm, Vector<Module*>&,
|
|||
}
|
||||
|
||||
// 16.2.1.9 FinishLoadingImportedModule ( referrer, specifier, payload, result ), https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
|
||||
void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleRequest const& module_request, ImportedModulePayload payload, ThrowCompletionOr<NonnullGCPtr<Module>> const& result)
|
||||
void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleRequest const& module_request, ImportedModulePayload payload, ThrowCompletionOr<GC::Ref<Module>> const& result)
|
||||
{
|
||||
// 1. If result is a normal completion, then
|
||||
if (!result.is_error()) {
|
||||
// NOTE: Only Script and CyclicModule referrers have the [[LoadedModules]] internal slot.
|
||||
if (referrer.has<NonnullGCPtr<Script>>() || referrer.has<NonnullGCPtr<CyclicModule>>()) {
|
||||
if (referrer.has<GC::Ref<Script>>() || referrer.has<GC::Ref<CyclicModule>>()) {
|
||||
auto& loaded_modules = referrer.visit(
|
||||
[](JS::NonnullGCPtr<JS::Realm>&) -> Vector<ModuleWithSpecifier>& {
|
||||
[](GC::Ref<JS::Realm>&) -> Vector<ModuleWithSpecifier>& {
|
||||
VERIFY_NOT_REACHED();
|
||||
__builtin_unreachable();
|
||||
},
|
||||
|
@ -101,19 +101,19 @@ void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleReque
|
|||
// i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
|
||||
loaded_modules.append(ModuleWithSpecifier {
|
||||
.specifier = module_request.module_specifier,
|
||||
.module = NonnullGCPtr<Module>(*module) });
|
||||
.module = GC::Ref<Module>(*module) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.has<NonnullGCPtr<GraphLoadingState>>()) {
|
||||
if (payload.has<GC::Ref<GraphLoadingState>>()) {
|
||||
// a. Perform ContinueModuleLoading(payload, result)
|
||||
continue_module_loading(payload.get<NonnullGCPtr<GraphLoadingState>>(), result);
|
||||
continue_module_loading(payload.get<GC::Ref<GraphLoadingState>>(), result);
|
||||
}
|
||||
// Else,
|
||||
else {
|
||||
// a. Perform ContinueDynamicImport(payload, result).
|
||||
continue_dynamic_import(payload.get<NonnullGCPtr<PromiseCapability>>(), result);
|
||||
continue_dynamic_import(payload.get<GC::Ref<PromiseCapability>>(), result);
|
||||
}
|
||||
|
||||
// 4. Return unused.
|
||||
|
@ -174,7 +174,7 @@ Object* Module::module_namespace_create(Vector<DeprecatedFlyString> unambiguous_
|
|||
auto module_namespace = realm.create<ModuleNamespaceObject>(realm, this, move(unambiguous_names));
|
||||
|
||||
// 9. Set module.[[Namespace]] to M.
|
||||
m_namespace = make_handle(module_namespace);
|
||||
m_namespace = make_root(module_namespace);
|
||||
|
||||
// 10. Return M.
|
||||
return module_namespace;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/DeprecatedFlyString.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibJS/ModuleLoading.h>
|
||||
#include <LibJS/Runtime/Environment.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
|
@ -37,7 +37,7 @@ struct ResolvedBinding {
|
|||
}
|
||||
|
||||
Type type { Null };
|
||||
GCPtr<Module> module;
|
||||
GC::Ptr<Module> module;
|
||||
DeprecatedFlyString export_name;
|
||||
|
||||
bool is_valid() const
|
||||
|
@ -58,25 +58,25 @@ struct ResolvedBinding {
|
|||
|
||||
// https://tc39.es/ecma262/#graphloadingstate-record
|
||||
struct GraphLoadingState : public Cell {
|
||||
JS_CELL(GraphLoadingState, Cell);
|
||||
JS_DECLARE_ALLOCATOR(GraphLoadingState);
|
||||
GC_CELL(GraphLoadingState, Cell);
|
||||
GC_DECLARE_ALLOCATOR(GraphLoadingState);
|
||||
|
||||
public:
|
||||
struct HostDefined : Cell {
|
||||
JS_CELL(HostDefined, Cell);
|
||||
GC_CELL(HostDefined, Cell);
|
||||
|
||||
public:
|
||||
virtual ~HostDefined() = default;
|
||||
};
|
||||
|
||||
GCPtr<PromiseCapability> promise_capability; // [[PromiseCapability]]
|
||||
bool is_loading { false }; // [[IsLoading]]
|
||||
size_t pending_module_count { 0 }; // [[PendingModulesCount]]
|
||||
HashTable<JS::GCPtr<CyclicModule>> visited; // [[Visited]]
|
||||
GCPtr<HostDefined> host_defined; // [[HostDefined]]
|
||||
GC::Ptr<PromiseCapability> promise_capability; // [[PromiseCapability]]
|
||||
bool is_loading { false }; // [[IsLoading]]
|
||||
size_t pending_module_count { 0 }; // [[PendingModulesCount]]
|
||||
HashTable<GC::Ptr<CyclicModule>> visited; // [[Visited]]
|
||||
GC::Ptr<HostDefined> host_defined; // [[HostDefined]]
|
||||
|
||||
private:
|
||||
GraphLoadingState(GCPtr<PromiseCapability> promise_capability, bool is_loading, size_t pending_module_count, HashTable<JS::GCPtr<CyclicModule>> visited, GCPtr<HostDefined> host_defined)
|
||||
GraphLoadingState(GC::Ptr<PromiseCapability> promise_capability, bool is_loading, size_t pending_module_count, HashTable<GC::Ptr<CyclicModule>> visited, GC::Ptr<HostDefined> host_defined)
|
||||
: promise_capability(move(promise_capability))
|
||||
, is_loading(is_loading)
|
||||
, pending_module_count(pending_module_count)
|
||||
|
@ -89,8 +89,8 @@ private:
|
|||
|
||||
// 16.2.1.4 Abstract Module Records, https://tc39.es/ecma262/#sec-abstract-module-records
|
||||
class Module : public Cell {
|
||||
JS_CELL(Module, Cell);
|
||||
JS_DECLARE_ALLOCATOR(Module);
|
||||
GC_CELL(Module, Cell);
|
||||
GC_DECLARE_ALLOCATOR(Module);
|
||||
|
||||
public:
|
||||
virtual ~Module() override;
|
||||
|
@ -115,7 +115,7 @@ public:
|
|||
virtual ThrowCompletionOr<u32> inner_module_linking(VM& vm, Vector<Module*>& stack, u32 index);
|
||||
virtual ThrowCompletionOr<u32> inner_module_evaluation(VM& vm, Vector<Module*>& stack, u32 index);
|
||||
|
||||
virtual PromiseCapability& load_requested_modules(GCPtr<GraphLoadingState::HostDefined>) = 0;
|
||||
virtual PromiseCapability& load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined>) = 0;
|
||||
|
||||
protected:
|
||||
Module(Realm&, ByteString filename, Script::HostDefined* host_defined = nullptr);
|
||||
|
@ -135,9 +135,9 @@ private:
|
|||
// destroy the VM but keep the modules this should not happen. Because VM
|
||||
// stores modules with a RefPtr we cannot just store the VM as that leads to
|
||||
// cycles.
|
||||
GCPtr<Realm> m_realm; // [[Realm]]
|
||||
GCPtr<Environment> m_environment; // [[Environment]]
|
||||
GCPtr<Object> m_namespace; // [[Namespace]]
|
||||
GC::Ptr<Realm> m_realm; // [[Realm]]
|
||||
GC::Ptr<Environment> m_environment; // [[Environment]]
|
||||
GC::Ptr<Object> m_namespace; // [[Namespace]]
|
||||
Script::HostDefined* m_host_defined { nullptr }; // [[HostDefined]]
|
||||
|
||||
// Needed for potential lookups of modules.
|
||||
|
@ -147,6 +147,6 @@ private:
|
|||
class CyclicModule;
|
||||
struct GraphLoadingState;
|
||||
|
||||
void finish_loading_imported_module(ImportedModuleReferrer, ModuleRequest const&, ImportedModulePayload, ThrowCompletionOr<NonnullGCPtr<Module>> const&);
|
||||
void finish_loading_imported_module(ImportedModuleReferrer, ModuleRequest const&, ImportedModulePayload, ThrowCompletionOr<GC::Ref<Module>> const&);
|
||||
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Variant.h>
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
using ImportedModuleReferrer = Variant<NonnullGCPtr<Script>, NonnullGCPtr<CyclicModule>, NonnullGCPtr<Realm>>;
|
||||
using ImportedModulePayload = Variant<NonnullGCPtr<GraphLoadingState>, NonnullGCPtr<PromiseCapability>>;
|
||||
using ImportedModuleReferrer = Variant<GC::Ref<Script>, GC::Ref<CyclicModule>, GC::Ref<Realm>>;
|
||||
using ImportedModulePayload = Variant<GC::Ref<GraphLoadingState>, GC::Ref<PromiseCapability>>;
|
||||
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ public:
|
|||
};
|
||||
|
||||
// Needs to mess with m_state, and we're not going to expose a non-const getter for that :^)
|
||||
friend ThrowCompletionOr<NonnullGCPtr<ECMAScriptFunctionObject>> FunctionConstructor::create_dynamic_function(VM&, FunctionObject&, FunctionObject*, FunctionKind, ReadonlySpan<String> parameter_args, String const& body_arg);
|
||||
friend ThrowCompletionOr<GC::Ref<ECMAScriptFunctionObject>> FunctionConstructor::create_dynamic_function(VM&, FunctionObject&, FunctionObject*, FunctionKind, ReadonlySpan<String> parameter_args, String const& body_arg);
|
||||
|
||||
static Parser parse_function_body_from_string(ByteString const& body_string, u16 parse_options, Vector<FunctionParameter> const& parameters, FunctionKind kind, FunctionParsingInsights&);
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ ThrowCompletionOr<Value> call_impl(VM&, FunctionObject& function, Value this_val
|
|||
}
|
||||
|
||||
// 7.3.15 Construct ( F [ , argumentsList [ , newTarget ] ] ), https://tc39.es/ecma262/#sec-construct
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> construct_impl(VM&, FunctionObject& function, ReadonlySpan<Value> arguments_list, FunctionObject* new_target)
|
||||
ThrowCompletionOr<GC::Ref<Object>> construct_impl(VM&, FunctionObject& function, ReadonlySpan<Value> arguments_list, FunctionObject* new_target)
|
||||
{
|
||||
// 1. If newTarget is not present, set newTarget to F.
|
||||
if (!new_target)
|
||||
|
@ -97,7 +97,7 @@ ThrowCompletionOr<size_t> length_of_array_like(VM& vm, Object const& object)
|
|||
}
|
||||
|
||||
// 7.3.20 CreateListFromArrayLike ( obj [ , elementTypes ] ), https://tc39.es/ecma262/#sec-createlistfromarraylike
|
||||
ThrowCompletionOr<MarkedVector<Value>> create_list_from_array_like(VM& vm, Value value, Function<ThrowCompletionOr<void>(Value)> check_value)
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> create_list_from_array_like(VM& vm, Value value, Function<ThrowCompletionOr<void>(Value)> check_value)
|
||||
{
|
||||
// 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».
|
||||
|
||||
|
@ -111,7 +111,7 @@ ThrowCompletionOr<MarkedVector<Value>> create_list_from_array_like(VM& vm, Value
|
|||
auto length = TRY(length_of_array_like(vm, array_like));
|
||||
|
||||
// 4. Let list be a new empty List.
|
||||
auto list = MarkedVector<Value> { vm.heap() };
|
||||
auto list = GC::MarkedVector<Value> { vm.heap() };
|
||||
list.ensure_capacity(length);
|
||||
|
||||
// 5. Let index be 0.
|
||||
|
@ -366,7 +366,7 @@ bool validate_and_apply_property_descriptor(Object* object, PropertyKey const& p
|
|||
}
|
||||
|
||||
// 10.1.14 GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ), https://tc39.es/ecma262/#sec-getprototypefromconstructor
|
||||
ThrowCompletionOr<Object*> get_prototype_from_constructor(VM& vm, FunctionObject const& constructor, NonnullGCPtr<Object> (Intrinsics::*intrinsic_default_prototype)())
|
||||
ThrowCompletionOr<Object*> get_prototype_from_constructor(VM& vm, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)())
|
||||
{
|
||||
// 1. Assert: intrinsicDefaultProto is this specification's name of an intrinsic object. The corresponding object must be an intrinsic that is intended to be used as the [[Prototype]] value of an object.
|
||||
|
||||
|
@ -387,7 +387,7 @@ ThrowCompletionOr<Object*> get_prototype_from_constructor(VM& vm, FunctionObject
|
|||
}
|
||||
|
||||
// 9.1.2.2 NewDeclarativeEnvironment ( E ), https://tc39.es/ecma262/#sec-newdeclarativeenvironment
|
||||
NonnullGCPtr<DeclarativeEnvironment> new_declarative_environment(Environment& environment)
|
||||
GC::Ref<DeclarativeEnvironment> new_declarative_environment(Environment& environment)
|
||||
{
|
||||
auto& heap = environment.heap();
|
||||
|
||||
|
@ -398,7 +398,7 @@ NonnullGCPtr<DeclarativeEnvironment> new_declarative_environment(Environment& en
|
|||
}
|
||||
|
||||
// 9.1.2.3 NewObjectEnvironment ( O, W, E ), https://tc39.es/ecma262/#sec-newobjectenvironment
|
||||
NonnullGCPtr<ObjectEnvironment> new_object_environment(Object& object, bool is_with_environment, Environment* environment)
|
||||
GC::Ref<ObjectEnvironment> new_object_environment(Object& object, bool is_with_environment, Environment* environment)
|
||||
{
|
||||
auto& heap = object.heap();
|
||||
|
||||
|
@ -411,7 +411,7 @@ NonnullGCPtr<ObjectEnvironment> new_object_environment(Object& object, bool is_w
|
|||
}
|
||||
|
||||
// 9.1.2.4 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/ecma262/#sec-newfunctionenvironment
|
||||
NonnullGCPtr<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject& function, Object* new_target)
|
||||
GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject& function, Object* new_target)
|
||||
{
|
||||
auto& heap = function.heap();
|
||||
|
||||
|
@ -439,7 +439,7 @@ NonnullGCPtr<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObj
|
|||
}
|
||||
|
||||
// 9.2.1.1 NewPrivateEnvironment ( outerPrivEnv ), https://tc39.es/ecma262/#sec-newprivateenvironment
|
||||
NonnullGCPtr<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer)
|
||||
GC::Ref<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer)
|
||||
{
|
||||
// 1. Let names be a new empty List.
|
||||
// 2. Return the PrivateEnvironment Record { [[OuterPrivateEnvironment]]: outerPrivEnv, [[Names]]: names }.
|
||||
|
@ -447,7 +447,7 @@ NonnullGCPtr<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironm
|
|||
}
|
||||
|
||||
// 9.4.3 GetThisEnvironment ( ), https://tc39.es/ecma262/#sec-getthisenvironment
|
||||
NonnullGCPtr<Environment> get_this_environment(VM& vm)
|
||||
GC::Ref<Environment> get_this_environment(VM& vm)
|
||||
{
|
||||
// 1. Let env be the running execution context's LexicalEnvironment.
|
||||
// 2. Repeat,
|
||||
|
@ -1482,7 +1482,7 @@ ThrowCompletionOr<DisposableResource> create_disposable_resource(VM& vm, Value v
|
|||
}
|
||||
|
||||
// 2.1.4 GetDisposeMethod ( V, hint ), https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod
|
||||
ThrowCompletionOr<GCPtr<FunctionObject>> get_dispose_method(VM& vm, Value value, Environment::InitializeBindingHint hint)
|
||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM& vm, Value value, Environment::InitializeBindingHint hint)
|
||||
{
|
||||
// NOTE: We only have sync dispose for now which means we ignore step 1.
|
||||
VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
|
||||
|
@ -1493,7 +1493,7 @@ ThrowCompletionOr<GCPtr<FunctionObject>> get_dispose_method(VM& vm, Value value,
|
|||
}
|
||||
|
||||
// 2.1.5 Dispose ( V, hint, method ), https://tc39.es/proposal-explicit-resource-management/#sec-dispose
|
||||
Completion dispose(VM& vm, Value value, NonnullGCPtr<FunctionObject> method)
|
||||
Completion dispose(VM& vm, Value value, GC::Ref<FunctionObject> method)
|
||||
{
|
||||
// 1. Let result be ? Call(method, V).
|
||||
[[maybe_unused]] auto result = TRY(call(vm, *method, value));
|
||||
|
@ -1550,7 +1550,7 @@ Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposabl
|
|||
return completion;
|
||||
}
|
||||
|
||||
Completion dispose_resources(VM& vm, GCPtr<DeclarativeEnvironment> disposable, Completion completion)
|
||||
Completion dispose_resources(VM& vm, GC::Ptr<DeclarativeEnvironment> disposable, Completion completion)
|
||||
{
|
||||
// 1. If disposable is not undefined, then
|
||||
if (disposable)
|
||||
|
@ -1588,12 +1588,12 @@ ThrowCompletionOr<Value> perform_import_call(VM& vm, Value specifier, Value opti
|
|||
|
||||
// 2. If referrer is null, set referrer to the current Realm Record.
|
||||
if (active_script_or_module.has<Empty>())
|
||||
return NonnullGCPtr<Realm> { realm };
|
||||
return GC::Ref<Realm> { realm };
|
||||
|
||||
if (active_script_or_module.has<NonnullGCPtr<Script>>())
|
||||
return active_script_or_module.get<NonnullGCPtr<Script>>();
|
||||
if (active_script_or_module.has<GC::Ref<Script>>())
|
||||
return active_script_or_module.get<GC::Ref<Script>>();
|
||||
|
||||
return NonnullGCPtr<CyclicModule> { verify_cast<CyclicModule>(*active_script_or_module.get<NonnullGCPtr<Module>>()) };
|
||||
return GC::Ref<CyclicModule> { verify_cast<CyclicModule>(*active_script_or_module.get<GC::Ref<Module>>()) };
|
||||
}();
|
||||
|
||||
// 7. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#include <AK/Concepts.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <LibCrypto/Forward.h>
|
||||
#include <LibGC/MarkedVector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/MarkedVector.h>
|
||||
#include <LibJS/Runtime/CanonicalIndex.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
|
@ -22,38 +22,38 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
NonnullGCPtr<DeclarativeEnvironment> new_declarative_environment(Environment&);
|
||||
NonnullGCPtr<ObjectEnvironment> new_object_environment(Object&, bool is_with_environment, Environment*);
|
||||
NonnullGCPtr<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject&, Object* new_target);
|
||||
NonnullGCPtr<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer);
|
||||
NonnullGCPtr<Environment> get_this_environment(VM&);
|
||||
GC::Ref<DeclarativeEnvironment> new_declarative_environment(Environment&);
|
||||
GC::Ref<ObjectEnvironment> new_object_environment(Object&, bool is_with_environment, Environment*);
|
||||
GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject&, Object* new_target);
|
||||
GC::Ref<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer);
|
||||
GC::Ref<Environment> get_this_environment(VM&);
|
||||
bool can_be_held_weakly(Value);
|
||||
Object* get_super_constructor(VM&);
|
||||
ThrowCompletionOr<Value> require_object_coercible(VM&, Value);
|
||||
ThrowCompletionOr<Value> call_impl(VM&, Value function, Value this_value, ReadonlySpan<Value> arguments = {});
|
||||
ThrowCompletionOr<Value> call_impl(VM&, FunctionObject& function, Value this_value, ReadonlySpan<Value> arguments = {});
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> construct_impl(VM&, FunctionObject&, ReadonlySpan<Value> arguments = {}, FunctionObject* new_target = nullptr);
|
||||
ThrowCompletionOr<GC::Ref<Object>> construct_impl(VM&, FunctionObject&, ReadonlySpan<Value> arguments = {}, FunctionObject* new_target = nullptr);
|
||||
ThrowCompletionOr<size_t> length_of_array_like(VM&, Object const&);
|
||||
ThrowCompletionOr<MarkedVector<Value>> create_list_from_array_like(VM&, Value, Function<ThrowCompletionOr<void>(Value)> = {});
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> create_list_from_array_like(VM&, Value, Function<ThrowCompletionOr<void>(Value)> = {});
|
||||
ThrowCompletionOr<FunctionObject*> species_constructor(VM&, Object const&, FunctionObject& default_constructor);
|
||||
ThrowCompletionOr<Realm*> get_function_realm(VM&, FunctionObject const&);
|
||||
ThrowCompletionOr<void> initialize_bound_name(VM&, DeprecatedFlyString const&, Value, Environment*);
|
||||
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)());
|
||||
ThrowCompletionOr<Object*> get_prototype_from_constructor(VM&, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)());
|
||||
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;
|
||||
NonnullGCPtr<FunctionObject> dispose_method;
|
||||
GC::Ref<FunctionObject> dispose_method;
|
||||
};
|
||||
ThrowCompletionOr<void> add_disposable_resource(VM&, Vector<DisposableResource>& disposable, Value, Environment::InitializeBindingHint, FunctionObject* = nullptr);
|
||||
ThrowCompletionOr<DisposableResource> create_disposable_resource(VM&, Value, Environment::InitializeBindingHint, FunctionObject* method = nullptr);
|
||||
ThrowCompletionOr<GCPtr<FunctionObject>> get_dispose_method(VM&, Value, Environment::InitializeBindingHint);
|
||||
Completion dispose(VM& vm, Value, NonnullGCPtr<FunctionObject> method);
|
||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM&, Value, Environment::InitializeBindingHint);
|
||||
Completion dispose(VM& vm, Value, GC::Ref<FunctionObject> method);
|
||||
Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposable, Completion completion);
|
||||
Completion dispose_resources(VM& vm, GCPtr<DeclarativeEnvironment> disposable, Completion completion);
|
||||
Completion dispose_resources(VM& vm, GC::Ptr<DeclarativeEnvironment> disposable, Completion completion);
|
||||
|
||||
ThrowCompletionOr<Value> perform_import_call(VM&, Value specifier, Value options_value);
|
||||
|
||||
|
@ -120,7 +120,7 @@ ALWAYS_INLINE ThrowCompletionOr<Value> call(VM& vm, FunctionObject& function, Va
|
|||
|
||||
// 7.3.15 Construct ( F [ , argumentsList [ , newTarget ] ] ), https://tc39.es/ecma262/#sec-construct
|
||||
template<typename... Args>
|
||||
ALWAYS_INLINE ThrowCompletionOr<NonnullGCPtr<Object>> construct(VM& vm, FunctionObject& function, Args&&... args)
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> construct(VM& vm, FunctionObject& function, Args&&... args)
|
||||
{
|
||||
constexpr auto argument_count = sizeof...(Args);
|
||||
if constexpr (argument_count > 0) {
|
||||
|
@ -131,19 +131,19 @@ ALWAYS_INLINE ThrowCompletionOr<NonnullGCPtr<Object>> construct(VM& vm, Function
|
|||
return construct_impl(vm, function);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThrowCompletionOr<NonnullGCPtr<Object>> construct(VM& vm, FunctionObject& function, ReadonlySpan<Value> arguments_list, FunctionObject* new_target = nullptr)
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> construct(VM& vm, FunctionObject& function, ReadonlySpan<Value> arguments_list, FunctionObject* new_target = nullptr)
|
||||
{
|
||||
return construct_impl(vm, function, arguments_list, new_target);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThrowCompletionOr<NonnullGCPtr<Object>> construct(VM& vm, FunctionObject& function, Span<Value> arguments_list, FunctionObject* new_target = nullptr)
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> construct(VM& vm, FunctionObject& function, Span<Value> arguments_list, FunctionObject* new_target = nullptr)
|
||||
{
|
||||
return construct_impl(vm, function, static_cast<ReadonlySpan<Value>>(arguments_list), new_target);
|
||||
}
|
||||
|
||||
// 10.1.13 OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ), https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
|
||||
template<typename T, typename... Args>
|
||||
ThrowCompletionOr<NonnullGCPtr<T>> ordinary_create_from_constructor(VM& vm, FunctionObject const& constructor, NonnullGCPtr<Object> (Intrinsics::*intrinsic_default_prototype)(), Args&&... args)
|
||||
ThrowCompletionOr<GC::Ref<T>> ordinary_create_from_constructor(VM& vm, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)(), Args&&... args)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
auto* prototype = TRY(get_prototype_from_constructor(vm, constructor, intrinsic_default_prototype));
|
||||
|
@ -199,7 +199,7 @@ void add_value_to_keyed_group(VM& vm, GroupsType& groups, KeyType key, Value val
|
|||
}
|
||||
|
||||
// 2. Let group be the Record { [[Key]]: key, [[Elements]]: « value » }.
|
||||
MarkedVector<Value> new_elements { vm.heap() };
|
||||
GC::MarkedVector<Value> new_elements { vm.heap() };
|
||||
new_elements.append(value);
|
||||
|
||||
// 3. Append group as the last element of groups.
|
||||
|
@ -278,7 +278,7 @@ ThrowCompletionOr<GroupsType> group_by(VM& vm, Value items, Value callback_funct
|
|||
// ii. Set key to CanonicalizeKeyedCollectionKey(key).
|
||||
key = canonicalize_keyed_collection_key(key.value());
|
||||
|
||||
add_value_to_keyed_group(vm, groups, make_handle(key.release_value()), value);
|
||||
add_value_to_keyed_group(vm, groups, make_root(key.release_value()), value);
|
||||
}
|
||||
|
||||
// i. Perform AddValueToKeyedGroup(groups, key, value).
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(Accessor);
|
||||
GC_DEFINE_ALLOCATOR(Accessor);
|
||||
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
namespace JS {
|
||||
|
||||
class Accessor final : public Cell {
|
||||
JS_CELL(Accessor, Cell);
|
||||
JS_DECLARE_ALLOCATOR(Accessor);
|
||||
GC_CELL(Accessor, Cell);
|
||||
GC_DECLARE_ALLOCATOR(Accessor);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<Accessor> create(VM& vm, FunctionObject* getter, FunctionObject* setter)
|
||||
static GC::Ref<Accessor> create(VM& vm, FunctionObject* getter, FunctionObject* setter)
|
||||
{
|
||||
return vm.heap().allocate<Accessor>(getter, setter);
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ private:
|
|||
{
|
||||
}
|
||||
|
||||
GCPtr<FunctionObject> m_getter;
|
||||
GCPtr<FunctionObject> m_setter;
|
||||
GC::Ptr<FunctionObject> m_getter;
|
||||
GC::Ptr<FunctionObject> m_setter;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AggregateError);
|
||||
GC_DEFINE_ALLOCATOR(AggregateError);
|
||||
|
||||
NonnullGCPtr<AggregateError> AggregateError::create(Realm& realm)
|
||||
GC::Ref<AggregateError> AggregateError::create(Realm& realm)
|
||||
{
|
||||
return realm.create<AggregateError>(realm.intrinsics().aggregate_error_prototype());
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ namespace JS {
|
|||
|
||||
class AggregateError : public Error {
|
||||
JS_OBJECT(AggregateError, Error);
|
||||
JS_DECLARE_ALLOCATOR(AggregateError);
|
||||
GC_DECLARE_ALLOCATOR(AggregateError);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<AggregateError> create(Realm&);
|
||||
static GC::Ref<AggregateError> create(Realm&);
|
||||
virtual ~AggregateError() override = default;
|
||||
|
||||
private:
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AggregateErrorConstructor);
|
||||
GC_DEFINE_ALLOCATOR(AggregateErrorConstructor);
|
||||
|
||||
AggregateErrorConstructor::AggregateErrorConstructor(Realm& realm)
|
||||
: NativeFunction(static_cast<Object&>(*realm.intrinsics().error_constructor()))
|
||||
|
@ -40,7 +40,7 @@ ThrowCompletionOr<Value> AggregateErrorConstructor::call()
|
|||
}
|
||||
|
||||
// 20.5.7.1.1 AggregateError ( errors, message [ , options ] ), https://tc39.es/ecma262/#sec-aggregate-error
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> AggregateErrorConstructor::construct(FunctionObject& new_target)
|
||||
ThrowCompletionOr<GC::Ref<Object>> AggregateErrorConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
auto& realm = *vm.current_realm();
|
||||
|
|
|
@ -12,14 +12,14 @@ namespace JS {
|
|||
|
||||
class AggregateErrorConstructor final : public NativeFunction {
|
||||
JS_OBJECT(AggregateErrorConstructor, NativeFunction);
|
||||
JS_DECLARE_ALLOCATOR(AggregateErrorConstructor);
|
||||
GC_DECLARE_ALLOCATOR(AggregateErrorConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~AggregateErrorConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<NonnullGCPtr<Object>> construct(FunctionObject& new_target) override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit AggregateErrorConstructor(Realm&);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AggregateErrorPrototype);
|
||||
GC_DEFINE_ALLOCATOR(AggregateErrorPrototype);
|
||||
|
||||
AggregateErrorPrototype::AggregateErrorPrototype(Realm& realm)
|
||||
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().error_prototype())
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace JS {
|
|||
|
||||
class AggregateErrorPrototype final : public Object {
|
||||
JS_OBJECT(AggregateErrorPrototype, Object);
|
||||
JS_DECLARE_ALLOCATOR(AggregateErrorPrototype);
|
||||
GC_DECLARE_ALLOCATOR(AggregateErrorPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArgumentsObject);
|
||||
GC_DEFINE_ALLOCATOR(ArgumentsObject);
|
||||
|
||||
ArgumentsObject::ArgumentsObject(Realm& realm, Environment& environment)
|
||||
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype(), MayInterfereWithIndexedPropertyAccess::Yes)
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace JS {
|
|||
|
||||
class ArgumentsObject final : public Object {
|
||||
JS_OBJECT(ArgumentsObject, Object);
|
||||
JS_DECLARE_ALLOCATOR(ArgumentsObject);
|
||||
GC_DECLARE_ALLOCATOR(ArgumentsObject);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
@ -36,8 +36,8 @@ private:
|
|||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
NonnullGCPtr<Environment> m_environment;
|
||||
GCPtr<Object> m_parameter_map;
|
||||
GC::Ref<Environment> m_environment;
|
||||
GC::Ptr<Object> m_parameter_map;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(Array);
|
||||
GC_DEFINE_ALLOCATOR(Array);
|
||||
|
||||
// 10.4.2.2 ArrayCreate ( length [ , proto ] ), https://tc39.es/ecma262/#sec-arraycreate
|
||||
ThrowCompletionOr<NonnullGCPtr<Array>> Array::create(Realm& realm, u64 length, Object* prototype)
|
||||
ThrowCompletionOr<GC::Ref<Array>> Array::create(Realm& realm, u64 length, Object* prototype)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
|
@ -45,7 +45,7 @@ ThrowCompletionOr<NonnullGCPtr<Array>> Array::create(Realm& realm, u64 length, O
|
|||
}
|
||||
|
||||
// 7.3.18 CreateArrayFromList ( elements ), https://tc39.es/ecma262/#sec-createarrayfromlist
|
||||
NonnullGCPtr<Array> Array::create_from(Realm& realm, ReadonlySpan<Value> elements)
|
||||
GC::Ref<Array> Array::create_from(Realm& realm, ReadonlySpan<Value> elements)
|
||||
{
|
||||
// 1. Let array be ! ArrayCreate(0).
|
||||
auto array = MUST(Array::create(realm, 0));
|
||||
|
@ -161,10 +161,10 @@ ThrowCompletionOr<bool> Array::set_length(PropertyDescriptor const& property_des
|
|||
}
|
||||
|
||||
// 23.1.3.30.1 SortIndexedProperties ( obj, len, SortCompare, holes ), https://tc39.es/ecma262/#sec-sortindexedproperties
|
||||
ThrowCompletionOr<MarkedVector<Value>> sort_indexed_properties(VM& vm, Object const& object, size_t length, Function<ThrowCompletionOr<double>(Value, Value)> const& sort_compare, Holes holes)
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> sort_indexed_properties(VM& vm, Object const& object, size_t length, Function<ThrowCompletionOr<double>(Value, Value)> const& sort_compare, Holes holes)
|
||||
{
|
||||
// 1. Let items be a new empty List.
|
||||
auto items = MarkedVector<Value> { vm.heap() };
|
||||
auto items = GC::MarkedVector<Value> { vm.heap() };
|
||||
|
||||
// 2. Let k be 0.
|
||||
// 3. Repeat, while k < len,
|
||||
|
@ -330,7 +330,7 @@ ThrowCompletionOr<bool> Array::internal_delete(PropertyKey const& property_key)
|
|||
}
|
||||
|
||||
// NON-STANDARD: Used to inject the ephemeral length property's key
|
||||
ThrowCompletionOr<MarkedVector<Value>> Array::internal_own_property_keys() const
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> Array::internal_own_property_keys() const
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
auto keys = TRY(Object::internal_own_property_keys());
|
||||
|
|
|
@ -21,23 +21,23 @@ namespace JS {
|
|||
|
||||
class Array : public Object {
|
||||
JS_OBJECT(Array, Object);
|
||||
JS_DECLARE_ALLOCATOR(Array);
|
||||
GC_DECLARE_ALLOCATOR(Array);
|
||||
|
||||
public:
|
||||
static ThrowCompletionOr<NonnullGCPtr<Array>> create(Realm&, u64 length, Object* prototype = nullptr);
|
||||
static NonnullGCPtr<Array> create_from(Realm&, ReadonlySpan<Value>);
|
||||
static ThrowCompletionOr<GC::Ref<Array>> create(Realm&, u64 length, Object* prototype = nullptr);
|
||||
static GC::Ref<Array> create_from(Realm&, ReadonlySpan<Value>);
|
||||
|
||||
template<size_t N>
|
||||
static NonnullGCPtr<Array> create_from(Realm& realm, Value const (&values)[N])
|
||||
static GC::Ref<Array> create_from(Realm& realm, Value const (&values)[N])
|
||||
{
|
||||
return create_from(realm, ReadonlySpan<Value> { values, N });
|
||||
}
|
||||
|
||||
// Non-standard but equivalent to CreateArrayFromList.
|
||||
template<typename T>
|
||||
static NonnullGCPtr<Array> create_from(Realm& realm, ReadonlySpan<T> elements, Function<Value(T const&)> map_fn)
|
||||
static GC::Ref<Array> create_from(Realm& realm, ReadonlySpan<T> elements, Function<Value(T const&)> map_fn)
|
||||
{
|
||||
auto values = MarkedVector<Value> { realm.heap() };
|
||||
auto values = GC::MarkedVector<Value> { realm.heap() };
|
||||
values.ensure_capacity(elements.size());
|
||||
for (auto const& element : elements)
|
||||
values.append(map_fn(element));
|
||||
|
@ -50,7 +50,7 @@ public:
|
|||
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override final;
|
||||
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override final;
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
|
||||
virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const override final;
|
||||
virtual ThrowCompletionOr<GC::MarkedVector<Value>> internal_own_property_keys() const override final;
|
||||
|
||||
[[nodiscard]] bool length_is_writable() const { return m_length_writable; }
|
||||
|
||||
|
@ -68,7 +68,7 @@ enum class Holes {
|
|||
ReadThroughHoles,
|
||||
};
|
||||
|
||||
ThrowCompletionOr<MarkedVector<Value>> sort_indexed_properties(VM&, Object const&, size_t length, Function<ThrowCompletionOr<double>(Value, Value)> const& sort_compare, Holes holes);
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> sort_indexed_properties(VM&, Object const&, size_t length, Function<ThrowCompletionOr<double>(Value, Value)> const& sort_compare, Holes holes);
|
||||
ThrowCompletionOr<double> compare_array_elements(VM&, Value x, Value y, FunctionObject* comparefn);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayBuffer);
|
||||
GC_DEFINE_ALLOCATOR(ArrayBuffer);
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<ArrayBuffer>> ArrayBuffer::create(Realm& realm, size_t byte_length)
|
||||
ThrowCompletionOr<GC::Ref<ArrayBuffer>> ArrayBuffer::create(Realm& realm, size_t byte_length)
|
||||
{
|
||||
auto buffer = ByteBuffer::create_zeroed(byte_length);
|
||||
if (buffer.is_error())
|
||||
|
@ -22,12 +22,12 @@ ThrowCompletionOr<NonnullGCPtr<ArrayBuffer>> ArrayBuffer::create(Realm& realm, s
|
|||
return realm.create<ArrayBuffer>(buffer.release_value(), realm.intrinsics().array_buffer_prototype());
|
||||
}
|
||||
|
||||
NonnullGCPtr<ArrayBuffer> ArrayBuffer::create(Realm& realm, ByteBuffer buffer)
|
||||
GC::Ref<ArrayBuffer> ArrayBuffer::create(Realm& realm, ByteBuffer buffer)
|
||||
{
|
||||
return realm.create<ArrayBuffer>(move(buffer), realm.intrinsics().array_buffer_prototype());
|
||||
}
|
||||
|
||||
NonnullGCPtr<ArrayBuffer> ArrayBuffer::create(Realm& realm, ByteBuffer* buffer)
|
||||
GC::Ref<ArrayBuffer> ArrayBuffer::create(Realm& realm, ByteBuffer* buffer)
|
||||
{
|
||||
return realm.create<ArrayBuffer>(buffer, realm.intrinsics().array_buffer_prototype());
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ ThrowCompletionOr<Optional<size_t>> get_array_buffer_max_byte_length_option(VM&
|
|||
}
|
||||
|
||||
// 25.2.2.1 AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] ), https://tc39.es/ecma262/#sec-allocatesharedarraybuffer
|
||||
ThrowCompletionOr<NonnullGCPtr<ArrayBuffer>> allocate_shared_array_buffer(VM& vm, FunctionObject& constructor, size_t byte_length)
|
||||
ThrowCompletionOr<GC::Ref<ArrayBuffer>> allocate_shared_array_buffer(VM& vm, FunctionObject& constructor, size_t byte_length)
|
||||
{
|
||||
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]] »).
|
||||
auto obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(vm, constructor, &Intrinsics::shared_array_buffer_prototype, nullptr));
|
||||
|
|
|
@ -56,12 +56,12 @@ struct DataBlock {
|
|||
|
||||
class ArrayBuffer : public Object {
|
||||
JS_OBJECT(ArrayBuffer, Object);
|
||||
JS_DECLARE_ALLOCATOR(ArrayBuffer);
|
||||
GC_DECLARE_ALLOCATOR(ArrayBuffer);
|
||||
|
||||
public:
|
||||
static ThrowCompletionOr<NonnullGCPtr<ArrayBuffer>> create(Realm&, size_t);
|
||||
static NonnullGCPtr<ArrayBuffer> create(Realm&, ByteBuffer);
|
||||
static NonnullGCPtr<ArrayBuffer> create(Realm&, ByteBuffer*);
|
||||
static ThrowCompletionOr<GC::Ref<ArrayBuffer>> create(Realm&, size_t);
|
||||
static GC::Ref<ArrayBuffer> create(Realm&, ByteBuffer);
|
||||
static GC::Ref<ArrayBuffer> create(Realm&, ByteBuffer*);
|
||||
|
||||
virtual ~ArrayBuffer() override = default;
|
||||
|
||||
|
@ -152,7 +152,7 @@ ThrowCompletionOr<ArrayBuffer*> array_buffer_copy_and_detach(VM&, ArrayBuffer& a
|
|||
ThrowCompletionOr<void> detach_array_buffer(VM&, ArrayBuffer& array_buffer, Optional<Value> key = {});
|
||||
ThrowCompletionOr<Optional<size_t>> get_array_buffer_max_byte_length_option(VM&, Value options);
|
||||
ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(VM&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length);
|
||||
ThrowCompletionOr<NonnullGCPtr<ArrayBuffer>> allocate_shared_array_buffer(VM&, FunctionObject& constructor, size_t byte_length);
|
||||
ThrowCompletionOr<GC::Ref<ArrayBuffer>> allocate_shared_array_buffer(VM&, FunctionObject& constructor, size_t byte_length);
|
||||
|
||||
// 25.1.3.2 ArrayBufferByteLength ( arrayBuffer, order ), https://tc39.es/ecma262/#sec-arraybufferbytelength
|
||||
inline size_t array_buffer_byte_length(ArrayBuffer const& array_buffer, ArrayBuffer::Order)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayBufferConstructor);
|
||||
GC_DEFINE_ALLOCATOR(ArrayBufferConstructor);
|
||||
|
||||
ArrayBufferConstructor::ArrayBufferConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.ArrayBuffer.as_string(), realm.intrinsics().function_prototype())
|
||||
|
@ -48,7 +48,7 @@ ThrowCompletionOr<Value> ArrayBufferConstructor::call()
|
|||
}
|
||||
|
||||
// 25.1.4.1 ArrayBuffer ( length [ , options ] ), https://tc39.es/ecma262/#sec-arraybuffer-length
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> ArrayBufferConstructor::construct(FunctionObject& new_target)
|
||||
ThrowCompletionOr<GC::Ref<Object>> ArrayBufferConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@ namespace JS {
|
|||
|
||||
class ArrayBufferConstructor final : public NativeFunction {
|
||||
JS_OBJECT(ArrayBufferConstructor, NativeFunction);
|
||||
JS_DECLARE_ALLOCATOR(ArrayBufferConstructor);
|
||||
GC_DECLARE_ALLOCATOR(ArrayBufferConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~ArrayBufferConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<NonnullGCPtr<Object>> construct(FunctionObject& new_target) override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit ArrayBufferConstructor(Realm&);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayBufferPrototype);
|
||||
GC_DEFINE_ALLOCATOR(ArrayBufferPrototype);
|
||||
|
||||
ArrayBufferPrototype::ArrayBufferPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().object_prototype())
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace JS {
|
|||
|
||||
class ArrayBufferPrototype final : public PrototypeObject<ArrayBufferPrototype, ArrayBuffer> {
|
||||
JS_PROTOTYPE_OBJECT(ArrayBufferPrototype, ArrayBuffer, ArrayBuffer);
|
||||
JS_DECLARE_ALLOCATOR(ArrayBufferPrototype);
|
||||
GC_DECLARE_ALLOCATOR(ArrayBufferPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayConstructor);
|
||||
GC_DEFINE_ALLOCATOR(ArrayConstructor);
|
||||
|
||||
ArrayConstructor::ArrayConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.Array.as_string(), realm.intrinsics().function_prototype())
|
||||
|
@ -57,7 +57,7 @@ ThrowCompletionOr<Value> ArrayConstructor::call()
|
|||
}
|
||||
|
||||
// 23.1.1.1 Array ( ...values ), https://tc39.es/ecma262/#sec-array
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> ArrayConstructor::construct(FunctionObject& new_target)
|
||||
ThrowCompletionOr<GC::Ref<Object>> ArrayConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
auto& realm = *vm.current_realm();
|
||||
|
@ -149,7 +149,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
|
|||
auto constructor = vm.this_value();
|
||||
|
||||
// 2. If mapfn is undefined, let mapping be false.
|
||||
GCPtr<FunctionObject> mapfn;
|
||||
GC::Ptr<FunctionObject> mapfn;
|
||||
|
||||
// 3. Else,
|
||||
if (!mapfn_value.is_undefined()) {
|
||||
|
@ -166,7 +166,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
|
|||
|
||||
// 5. If usingIterator is not undefined, then
|
||||
if (using_iterator) {
|
||||
GCPtr<Object> array;
|
||||
GC::Ptr<Object> array;
|
||||
|
||||
// a. If IsConstructor(C) is true, then
|
||||
if (constructor.is_constructor()) {
|
||||
|
@ -245,7 +245,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
|
|||
// 8. Let len be ? LengthOfArrayLike(arrayLike).
|
||||
auto length = TRY(length_of_array_like(vm, array_like));
|
||||
|
||||
GCPtr<Object> array;
|
||||
GC::Ptr<Object> array;
|
||||
|
||||
// 9. If IsConstructor(C) is true, then
|
||||
if (constructor.is_constructor()) {
|
||||
|
@ -306,7 +306,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
|
||||
|
||||
// 3. Let fromAsyncClosure be a new Abstract Closure with no parameters that captures C, mapfn, and thisArg and performs the following steps when called:
|
||||
auto from_async_closure = create_heap_function(realm.heap(), [constructor, mapfn, this_arg, &vm, &realm, async_items]() mutable -> Completion {
|
||||
auto from_async_closure = GC::create_function(realm.heap(), [constructor, mapfn, this_arg, &vm, &realm, async_items]() mutable -> Completion {
|
||||
bool mapping;
|
||||
|
||||
// a. If mapfn is undefined, let mapping be false.
|
||||
|
@ -326,7 +326,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
// c. Let usingAsyncIterator be ? GetMethod(asyncItems, @@asyncIterator).
|
||||
auto using_async_iterator = TRY(async_items.get_method(vm, vm.well_known_symbol_async_iterator()));
|
||||
|
||||
GCPtr<FunctionObject> using_sync_iterator;
|
||||
GC::Ptr<FunctionObject> using_sync_iterator;
|
||||
|
||||
// d. If usingAsyncIterator is undefined, then
|
||||
if (!using_async_iterator) {
|
||||
|
@ -335,7 +335,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
}
|
||||
|
||||
// e. Let iteratorRecord be undefined.
|
||||
GCPtr<IteratorRecord> iterator_record;
|
||||
GC::Ptr<IteratorRecord> iterator_record;
|
||||
|
||||
// f. If usingAsyncIterator is not undefined, then
|
||||
if (using_async_iterator) {
|
||||
|
@ -352,7 +352,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
|
||||
// h. If iteratorRecord is not undefined, then
|
||||
if (iterator_record) {
|
||||
GCPtr<Object> array;
|
||||
GC::Ptr<Object> array;
|
||||
|
||||
// i. If IsConstructor(C) is true, then
|
||||
if (constructor.is_constructor()) {
|
||||
|
@ -458,7 +458,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
// iii. Let len be ? LengthOfArrayLike(arrayLike).
|
||||
auto length = TRY(length_of_array_like(vm, array_like));
|
||||
|
||||
GCPtr<Object> array;
|
||||
GC::Ptr<Object> array;
|
||||
|
||||
// iv. If IsConstructor(C) is true, then
|
||||
if (constructor.is_constructor()) {
|
||||
|
@ -542,7 +542,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::of)
|
|||
// 3. Let C be the this value.
|
||||
auto constructor = vm.this_value();
|
||||
|
||||
GCPtr<Object> array;
|
||||
GC::Ptr<Object> array;
|
||||
|
||||
// 4. If IsConstructor(C) is true, then
|
||||
if (constructor.is_constructor()) {
|
||||
|
|
|
@ -12,14 +12,14 @@ namespace JS {
|
|||
|
||||
class ArrayConstructor final : public NativeFunction {
|
||||
JS_OBJECT(ArrayConstructor, NativeFunction);
|
||||
JS_DECLARE_ALLOCATOR(ArrayConstructor);
|
||||
GC_DECLARE_ALLOCATOR(ArrayConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~ArrayConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<NonnullGCPtr<Object>> construct(FunctionObject& new_target) override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit ArrayConstructor(Realm&);
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayIterator);
|
||||
GC_DEFINE_ALLOCATOR(ArrayIterator);
|
||||
|
||||
NonnullGCPtr<ArrayIterator> ArrayIterator::create(Realm& realm, Value array, Object::PropertyKind iteration_kind)
|
||||
GC::Ref<ArrayIterator> ArrayIterator::create(Realm& realm, Value array, Object::PropertyKind iteration_kind)
|
||||
{
|
||||
return realm.create<ArrayIterator>(array, iteration_kind, realm.intrinsics().array_iterator_prototype());
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ namespace JS {
|
|||
|
||||
class ArrayIterator final : public Object {
|
||||
JS_OBJECT(ArrayIterator, Object);
|
||||
JS_DECLARE_ALLOCATOR(ArrayIterator);
|
||||
GC_DECLARE_ALLOCATOR(ArrayIterator);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<ArrayIterator> create(Realm&, Value array, Object::PropertyKind iteration_kind);
|
||||
static GC::Ref<ArrayIterator> create(Realm&, Value array, Object::PropertyKind iteration_kind);
|
||||
|
||||
virtual ~ArrayIterator() override = default;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayIteratorPrototype);
|
||||
GC_DEFINE_ALLOCATOR(ArrayIteratorPrototype);
|
||||
|
||||
ArrayIteratorPrototype::ArrayIteratorPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().iterator_prototype())
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace JS {
|
|||
|
||||
class ArrayIteratorPrototype final : public PrototypeObject<ArrayIteratorPrototype, ArrayIterator> {
|
||||
JS_PROTOTYPE_OBJECT(ArrayIteratorPrototype, ArrayIterator, ArrayIterator);
|
||||
JS_DECLARE_ALLOCATOR(ArrayIteratorPrototype);
|
||||
GC_DECLARE_ALLOCATOR(ArrayIteratorPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ArrayPrototype);
|
||||
GC_DEFINE_ALLOCATOR(ArrayPrototype);
|
||||
|
||||
static HashTable<NonnullGCPtr<Object>> s_array_join_seen_objects;
|
||||
static HashTable<GC::Ref<Object>> s_array_join_seen_objects;
|
||||
|
||||
ArrayPrototype::ArrayPrototype(Realm& realm)
|
||||
: Array(realm.intrinsics().object_prototype())
|
||||
|
@ -1339,15 +1339,15 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::some)
|
|||
return Value(false);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> array_merge_sort(VM& vm, Function<ThrowCompletionOr<double>(Value, Value)> const& compare_func, MarkedVector<Value>& arr_to_sort)
|
||||
ThrowCompletionOr<void> array_merge_sort(VM& vm, Function<ThrowCompletionOr<double>(Value, Value)> const& compare_func, GC::MarkedVector<Value>& arr_to_sort)
|
||||
{
|
||||
// FIXME: it would probably be better to switch to insertion sort for small arrays for
|
||||
// better performance
|
||||
if (arr_to_sort.size() <= 1)
|
||||
return {};
|
||||
|
||||
MarkedVector<Value> left(vm.heap());
|
||||
MarkedVector<Value> right(vm.heap());
|
||||
GC::MarkedVector<Value> left(vm.heap());
|
||||
GC::MarkedVector<Value> right(vm.heap());
|
||||
|
||||
left.ensure_capacity(arr_to_sort.size() / 2);
|
||||
right.ensure_capacity(arr_to_sort.size() / 2 + (arr_to_sort.size() & 1));
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace JS {
|
|||
|
||||
class ArrayPrototype final : public Array {
|
||||
JS_OBJECT(ArrayPrototype, Array);
|
||||
JS_DECLARE_ALLOCATOR(ArrayPrototype);
|
||||
GC_DECLARE_ALLOCATOR(ArrayPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
@ -64,6 +64,6 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(with);
|
||||
};
|
||||
|
||||
ThrowCompletionOr<void> array_merge_sort(VM&, Function<ThrowCompletionOr<double>(Value, Value)> const& compare_func, MarkedVector<Value>& arr_to_sort);
|
||||
ThrowCompletionOr<void> array_merge_sort(VM&, Function<ThrowCompletionOr<double>(Value, Value)> const& compare_func, GC::MarkedVector<Value>& arr_to_sort);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncFromSyncIterator);
|
||||
GC_DEFINE_ALLOCATOR(AsyncFromSyncIterator);
|
||||
|
||||
NonnullGCPtr<AsyncFromSyncIterator> AsyncFromSyncIterator::create(Realm& realm, NonnullGCPtr<IteratorRecord> sync_iterator_record)
|
||||
GC::Ref<AsyncFromSyncIterator> AsyncFromSyncIterator::create(Realm& realm, GC::Ref<IteratorRecord> sync_iterator_record)
|
||||
{
|
||||
return realm.create<AsyncFromSyncIterator>(realm, sync_iterator_record);
|
||||
}
|
||||
|
||||
AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, NonnullGCPtr<IteratorRecord> sync_iterator_record)
|
||||
AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, GC::Ref<IteratorRecord> sync_iterator_record)
|
||||
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().async_from_sync_iterator_prototype())
|
||||
, m_sync_iterator_record(sync_iterator_record)
|
||||
{
|
||||
|
|
|
@ -15,10 +15,10 @@ namespace JS {
|
|||
// 27.1.4.3 Properties of Async-from-Sync Iterator Instances, https://tc39.es/ecma262/#sec-properties-of-async-from-sync-iterator-instances
|
||||
class AsyncFromSyncIterator final : public Object {
|
||||
JS_OBJECT(AsyncFromSyncIterator, Object);
|
||||
JS_DECLARE_ALLOCATOR(AsyncFromSyncIterator);
|
||||
GC_DECLARE_ALLOCATOR(AsyncFromSyncIterator);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<AsyncFromSyncIterator> create(Realm&, NonnullGCPtr<IteratorRecord> sync_iterator_record);
|
||||
static GC::Ref<AsyncFromSyncIterator> create(Realm&, GC::Ref<IteratorRecord> sync_iterator_record);
|
||||
|
||||
virtual ~AsyncFromSyncIterator() override = default;
|
||||
|
||||
|
@ -28,9 +28,9 @@ public:
|
|||
IteratorRecord const& sync_iterator_record() const { return m_sync_iterator_record; }
|
||||
|
||||
private:
|
||||
AsyncFromSyncIterator(Realm&, NonnullGCPtr<IteratorRecord> sync_iterator_record);
|
||||
AsyncFromSyncIterator(Realm&, GC::Ref<IteratorRecord> sync_iterator_record);
|
||||
|
||||
NonnullGCPtr<IteratorRecord> m_sync_iterator_record; // [[SyncIteratorRecord]]
|
||||
GC::Ref<IteratorRecord> m_sync_iterator_record; // [[SyncIteratorRecord]]
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncFromSyncIteratorPrototype);
|
||||
GC_DEFINE_ALLOCATOR(AsyncFromSyncIteratorPrototype);
|
||||
|
||||
AsyncFromSyncIteratorPrototype::AsyncFromSyncIteratorPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().async_iterator_prototype())
|
||||
|
@ -199,7 +199,7 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::throw_)
|
|||
}
|
||||
|
||||
// 27.1.4.1 CreateAsyncFromSyncIterator ( syncIteratorRecord ), https://tc39.es/ecma262/#sec-createasyncfromsynciterator
|
||||
NonnullGCPtr<IteratorRecord> create_async_from_sync_iterator(VM& vm, NonnullGCPtr<IteratorRecord> sync_iterator_record)
|
||||
GC::Ref<IteratorRecord> create_async_from_sync_iterator(VM& vm, GC::Ref<IteratorRecord> sync_iterator_record)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace JS {
|
|||
// 27.1.4.2 The %AsyncFromSyncIteratorPrototype% Object, https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%-object
|
||||
class AsyncFromSyncIteratorPrototype final : public PrototypeObject<AsyncFromSyncIteratorPrototype, AsyncFromSyncIterator> {
|
||||
JS_PROTOTYPE_OBJECT(AsyncFromSyncIteratorPrototype, AsyncFromSyncIterator, AsyncFromSyncIterator);
|
||||
JS_DECLARE_ALLOCATOR(AsyncFromSyncIteratorPrototype);
|
||||
GC_DECLARE_ALLOCATOR(AsyncFromSyncIteratorPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
@ -31,6 +31,6 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(throw_);
|
||||
};
|
||||
|
||||
NonnullGCPtr<IteratorRecord> create_async_from_sync_iterator(VM&, NonnullGCPtr<IteratorRecord> sync_iterator);
|
||||
GC::Ref<IteratorRecord> create_async_from_sync_iterator(VM&, GC::Ref<IteratorRecord> sync_iterator);
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncFunctionConstructor);
|
||||
GC_DEFINE_ALLOCATOR(AsyncFunctionConstructor);
|
||||
|
||||
AsyncFunctionConstructor::AsyncFunctionConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.AsyncFunction.as_string(), realm.intrinsics().function_constructor())
|
||||
|
@ -37,7 +37,7 @@ ThrowCompletionOr<Value> AsyncFunctionConstructor::call()
|
|||
}
|
||||
|
||||
// 27.7.1.1 AsyncFunction ( ...parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-async-function-constructor-arguments
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> AsyncFunctionConstructor::construct(FunctionObject& new_target)
|
||||
ThrowCompletionOr<GC::Ref<Object>> AsyncFunctionConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@ namespace JS {
|
|||
|
||||
class AsyncFunctionConstructor final : public NativeFunction {
|
||||
JS_OBJECT(AsyncFunctionConstructor, NativeFunction);
|
||||
JS_DECLARE_ALLOCATOR(AsyncFunctionConstructor);
|
||||
GC_DECLARE_ALLOCATOR(AsyncFunctionConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~AsyncFunctionConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<NonnullGCPtr<Object>> construct(FunctionObject& new_target) override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit AsyncFunctionConstructor(Realm&);
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncFunctionDriverWrapper);
|
||||
GC_DEFINE_ALLOCATOR(AsyncFunctionDriverWrapper);
|
||||
|
||||
NonnullGCPtr<Promise> AsyncFunctionDriverWrapper::create(Realm& realm, GeneratorObject* generator_object)
|
||||
GC::Ref<Promise> AsyncFunctionDriverWrapper::create(Realm& realm, GeneratorObject* generator_object)
|
||||
{
|
||||
auto top_level_promise = Promise::create(realm);
|
||||
// Note: The top_level_promise is also kept alive by this Wrapper
|
||||
|
@ -29,7 +29,7 @@ NonnullGCPtr<Promise> AsyncFunctionDriverWrapper::create(Realm& realm, Generator
|
|||
return top_level_promise;
|
||||
}
|
||||
|
||||
AsyncFunctionDriverWrapper::AsyncFunctionDriverWrapper(Realm& realm, NonnullGCPtr<GeneratorObject> generator_object, NonnullGCPtr<Promise> top_level_promise)
|
||||
AsyncFunctionDriverWrapper::AsyncFunctionDriverWrapper(Realm& realm, GC::Ref<GeneratorObject> generator_object, GC::Ref<Promise> top_level_promise)
|
||||
: Promise(realm.intrinsics().promise_prototype())
|
||||
, m_generator_object(generator_object)
|
||||
, m_top_level_promise(top_level_promise)
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace JS {
|
|||
|
||||
class AsyncFunctionDriverWrapper final : public Promise {
|
||||
JS_OBJECT(AsyncFunctionDriverWrapper, Promise);
|
||||
JS_DECLARE_ALLOCATOR(AsyncFunctionDriverWrapper);
|
||||
GC_DECLARE_ALLOCATOR(AsyncFunctionDriverWrapper);
|
||||
|
||||
public:
|
||||
enum class IsInitialExecution {
|
||||
|
@ -24,7 +24,7 @@ public:
|
|||
Yes,
|
||||
};
|
||||
|
||||
[[nodiscard]] static NonnullGCPtr<Promise> create(Realm&, GeneratorObject*);
|
||||
[[nodiscard]] static GC::Ref<Promise> create(Realm&, GeneratorObject*);
|
||||
|
||||
virtual ~AsyncFunctionDriverWrapper() override = default;
|
||||
void visit_edges(Cell::Visitor&) override;
|
||||
|
@ -32,12 +32,12 @@ public:
|
|||
void continue_async_execution(VM&, Value, bool is_successful, IsInitialExecution is_initial_execution = IsInitialExecution::No);
|
||||
|
||||
private:
|
||||
AsyncFunctionDriverWrapper(Realm&, NonnullGCPtr<GeneratorObject>, NonnullGCPtr<Promise> top_level_promise);
|
||||
AsyncFunctionDriverWrapper(Realm&, GC::Ref<GeneratorObject>, GC::Ref<Promise> top_level_promise);
|
||||
ThrowCompletionOr<void> await(Value);
|
||||
|
||||
NonnullGCPtr<GeneratorObject> m_generator_object;
|
||||
NonnullGCPtr<Promise> m_top_level_promise;
|
||||
GCPtr<Promise> m_current_promise { nullptr };
|
||||
GC::Ref<GeneratorObject> m_generator_object;
|
||||
GC::Ref<Promise> m_top_level_promise;
|
||||
GC::Ptr<Promise> m_current_promise { nullptr };
|
||||
OwnPtr<ExecutionContext> m_suspended_execution_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncFunctionPrototype);
|
||||
GC_DEFINE_ALLOCATOR(AsyncFunctionPrototype);
|
||||
|
||||
AsyncFunctionPrototype::AsyncFunctionPrototype(Realm& realm)
|
||||
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().function_prototype())
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace JS {
|
|||
|
||||
class AsyncFunctionPrototype final : public Object {
|
||||
JS_OBJECT(AsyncFunctionPrototype, Object);
|
||||
JS_DECLARE_ALLOCATOR(AsyncFunctionPrototype);
|
||||
GC_DECLARE_ALLOCATOR(AsyncFunctionPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncGenerator);
|
||||
GC_DEFINE_ALLOCATOR(AsyncGenerator);
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
ThrowCompletionOr<GC::Ref<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
|
@ -51,7 +51,7 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
|
|||
}
|
||||
|
||||
// 27.6.3.4 AsyncGeneratorEnqueue ( generator, completion, promiseCapability ), https://tc39.es/ecma262/#sec-asyncgeneratorenqueue
|
||||
void AsyncGenerator::async_generator_enqueue(Completion completion, NonnullGCPtr<PromiseCapability> promise_capability)
|
||||
void AsyncGenerator::async_generator_enqueue(Completion completion, GC::Ref<PromiseCapability> promise_capability)
|
||||
{
|
||||
// 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }.
|
||||
auto request = AsyncGeneratorRequest { .completion = move(completion), .capability = promise_capability };
|
||||
|
@ -458,7 +458,7 @@ void AsyncGenerator::complete_step(Completion completion, bool done, Realm* real
|
|||
// a. Assert: completion.[[Type]] is normal.
|
||||
VERIFY(completion.type() == Completion::Type::Normal);
|
||||
|
||||
GCPtr<Object> iterator_result;
|
||||
GC::Ptr<Object> iterator_result;
|
||||
|
||||
// b. If realm is present, then
|
||||
if (realm) {
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace JS {
|
|||
// 27.6.2 Properties of AsyncGenerator Instances, https://tc39.es/ecma262/#sec-properties-of-asyncgenerator-intances
|
||||
class AsyncGenerator final : public Object {
|
||||
JS_OBJECT(AsyncGenerator, Object);
|
||||
JS_DECLARE_ALLOCATOR(AsyncGenerator);
|
||||
GC_DECLARE_ALLOCATOR(AsyncGenerator);
|
||||
|
||||
public:
|
||||
enum class State {
|
||||
|
@ -28,11 +28,11 @@ public:
|
|||
Completed,
|
||||
};
|
||||
|
||||
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>);
|
||||
static ThrowCompletionOr<GC::Ref<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>);
|
||||
|
||||
virtual ~AsyncGenerator() override;
|
||||
|
||||
void async_generator_enqueue(Completion, NonnullGCPtr<PromiseCapability>);
|
||||
void async_generator_enqueue(Completion, GC::Ref<PromiseCapability>);
|
||||
ThrowCompletionOr<void> resume(VM&, Completion completion);
|
||||
void await_return();
|
||||
void complete_step(Completion, bool done, Realm* realm = nullptr);
|
||||
|
@ -58,9 +58,9 @@ private:
|
|||
Vector<AsyncGeneratorRequest> m_async_generator_queue; // [[AsyncGeneratorQueue]]
|
||||
Optional<String> m_generator_brand; // [[GeneratorBrand]]
|
||||
|
||||
GCPtr<ECMAScriptFunctionObject> m_generating_function;
|
||||
GC::Ptr<ECMAScriptFunctionObject> m_generating_function;
|
||||
Value m_previous_value;
|
||||
GCPtr<Promise> m_current_promise;
|
||||
GC::Ptr<Promise> m_current_promise;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncGeneratorFunctionConstructor);
|
||||
GC_DEFINE_ALLOCATOR(AsyncGeneratorFunctionConstructor);
|
||||
|
||||
AsyncGeneratorFunctionConstructor::AsyncGeneratorFunctionConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.AsyncGeneratorFunction.as_string(), realm.intrinsics().function_prototype())
|
||||
|
@ -38,7 +38,7 @@ ThrowCompletionOr<Value> AsyncGeneratorFunctionConstructor::call()
|
|||
}
|
||||
|
||||
// 27.4.1.1 AsyncGeneratorFunction ( ...parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-asyncgeneratorfunction
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> AsyncGeneratorFunctionConstructor::construct(FunctionObject& new_target)
|
||||
ThrowCompletionOr<GC::Ref<Object>> AsyncGeneratorFunctionConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@ namespace JS {
|
|||
|
||||
class AsyncGeneratorFunctionConstructor final : public NativeFunction {
|
||||
JS_OBJECT(AsyncGeneratorFunctionConstructor, NativeFunction);
|
||||
JS_DECLARE_ALLOCATOR(AsyncGeneratorFunctionConstructor);
|
||||
GC_DECLARE_ALLOCATOR(AsyncGeneratorFunctionConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~AsyncGeneratorFunctionConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<NonnullGCPtr<Object>> construct(FunctionObject& new_target) override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit AsyncGeneratorFunctionConstructor(Realm&);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncGeneratorFunctionPrototype);
|
||||
GC_DEFINE_ALLOCATOR(AsyncGeneratorFunctionPrototype);
|
||||
|
||||
AsyncGeneratorFunctionPrototype::AsyncGeneratorFunctionPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().function_prototype())
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace JS {
|
|||
|
||||
class AsyncGeneratorFunctionPrototype final : public PrototypeObject<AsyncGeneratorFunctionPrototype, AsyncGeneratorFunction> {
|
||||
JS_PROTOTYPE_OBJECT(AsyncGeneratorFunctionPrototype, AsyncGeneratorFunction, AsyncGeneratorFunction);
|
||||
JS_DECLARE_ALLOCATOR(AsyncGeneratorFunctionPrototype);
|
||||
GC_DECLARE_ALLOCATOR(AsyncGeneratorFunctionPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(AsyncGeneratorPrototype);
|
||||
GC_DEFINE_ALLOCATOR(AsyncGeneratorPrototype);
|
||||
|
||||
// 27.6.1 Properties of the AsyncGenerator Prototype Object, https://tc39.es/ecma262/#sec-properties-of-asyncgenerator-prototype
|
||||
AsyncGeneratorPrototype::AsyncGeneratorPrototype(Realm& realm)
|
||||
|
@ -34,7 +34,7 @@ void AsyncGeneratorPrototype::initialize(Realm& realm)
|
|||
}
|
||||
|
||||
// 27.6.3.3 AsyncGeneratorValidate ( generator, generatorBrand ), https://tc39.es/ecma262/#sec-asyncgeneratorvalidate
|
||||
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> async_generator_validate(VM& vm, Value generator, Optional<String> generator_brand)
|
||||
static ThrowCompletionOr<GC::Ref<AsyncGenerator>> async_generator_validate(VM& vm, Value generator, Optional<String> generator_brand)
|
||||
{
|
||||
// 1. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]).
|
||||
// 2. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorState]]).
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace JS {
|
|||
|
||||
class AsyncGeneratorPrototype final : public PrototypeObject<AsyncGeneratorPrototype, AsyncGenerator> {
|
||||
JS_PROTOTYPE_OBJECT(AsyncGeneratorPrototype, AsyncGenerator, AsyncGenerator)
|
||||
JS_DECLARE_ALLOCATOR(AsyncGeneratorPrototype);
|
||||
GC_DECLARE_ALLOCATOR(AsyncGeneratorPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -13,8 +13,8 @@ namespace JS {
|
|||
|
||||
// 27.6.3.1 AsyncGeneratorRequest Records, https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records
|
||||
struct AsyncGeneratorRequest {
|
||||
Completion completion; // [[Completion]]
|
||||
NonnullGCPtr<PromiseCapability> capability; // [[Capability]]
|
||||
Completion completion; // [[Completion]]
|
||||
GC::Ref<PromiseCapability> capability; // [[Capability]]
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue