summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Tests/LibJS/test-js.cpp36
-rw-r--r--Userland/Libraries/LibJS/Heap/Heap.cpp11
-rw-r--r--Userland/Libraries/LibJS/Heap/Heap.h4
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.cleanupSome.js10
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js11
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.prototype.add.js11
6 files changed, 71 insertions, 12 deletions
diff --git a/Tests/LibJS/test-js.cpp b/Tests/LibJS/test-js.cpp
index 242c004e29..5bd418c769 100644
--- a/Tests/LibJS/test-js.cpp
+++ b/Tests/LibJS/test-js.cpp
@@ -58,6 +58,42 @@ TESTJS_GLOBAL_FUNCTION(get_weak_map_size, getWeakMapSize)
return JS::Value(weak_map->values().size());
}
+TESTJS_GLOBAL_FUNCTION(mark_as_garbage, markAsGarbage)
+{
+ auto argument = vm.argument(0);
+ if (!argument.is_string()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAString, argument.to_string_without_side_effects());
+ return {};
+ }
+
+ auto& variable_name = argument.as_string();
+
+ // In native functions we don't have a lexical environment so get the outer via the execution stack.
+ auto outer_environment = vm.execution_context_stack().last_matching([&](auto& execution_context) {
+ return execution_context->lexical_environment != nullptr;
+ });
+ if (!outer_environment.has_value()) {
+ vm.throw_exception<JS::ReferenceError>(global_object, JS::ErrorType::UnknownIdentifier, variable_name.string());
+ return {};
+ }
+
+ auto variable = outer_environment.value()->lexical_environment->get_from_environment(variable_name.string());
+ if (!variable.has_value()) {
+ vm.throw_exception<JS::ReferenceError>(global_object, JS::ErrorType::UnknownIdentifier, variable_name.string());
+ return {};
+ }
+
+ if (!variable->value.is_object()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, String::formatted("Variable with name {}", variable_name.string()));
+ return {};
+ }
+
+ vm.heap().uproot_cell(&variable->value.as_object());
+ outer_environment.value()->lexical_environment->delete_from_environment(variable_name.string());
+
+ return JS::js_undefined();
+}
+
TESTJS_RUN_FILE_FUNCTION(const String& test_file, JS::Interpreter&)
{
if (!test262_parser_tests)
diff --git a/Userland/Libraries/LibJS/Heap/Heap.cpp b/Userland/Libraries/LibJS/Heap/Heap.cpp
index 3329489e0e..d0bc3ba289 100644
--- a/Userland/Libraries/LibJS/Heap/Heap.cpp
+++ b/Userland/Libraries/LibJS/Heap/Heap.cpp
@@ -191,9 +191,15 @@ public:
void Heap::mark_live_cells(const HashTable<Cell*>& roots)
{
dbgln_if(HEAP_DEBUG, "mark_live_cells:");
+
MarkingVisitor visitor;
for (auto* root : roots)
visitor.visit(root);
+
+ for (auto& inverse_root : m_uprooted_cells)
+ inverse_root->set_marked(false);
+
+ m_uprooted_cells.clear();
}
void Heap::sweep_dead_cells(bool print_report, const Core::ElapsedTimer& measurement_timer)
@@ -327,4 +333,9 @@ void Heap::undefer_gc(Badge<DeferGC>)
}
}
+void Heap::uproot_cell(Cell* cell)
+{
+ m_uprooted_cells.append(cell);
+}
+
}
diff --git a/Userland/Libraries/LibJS/Heap/Heap.h b/Userland/Libraries/LibJS/Heap/Heap.h
index 157eff0f3a..8f7ac0d39e 100644
--- a/Userland/Libraries/LibJS/Heap/Heap.h
+++ b/Userland/Libraries/LibJS/Heap/Heap.h
@@ -83,6 +83,8 @@ public:
BlockAllocator& block_allocator() { return m_block_allocator; }
+ void uproot_cell(Cell* cell);
+
private:
Cell* allocate_cell(size_t);
@@ -117,6 +119,8 @@ private:
WeakContainer::List m_weak_containers;
+ Vector<Cell*> m_uprooted_cells;
+
BlockAllocator m_block_allocator;
size_t m_gc_deferrals { 0 };
diff --git a/Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.cleanupSome.js b/Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.cleanupSome.js
index e5b7e5c46f..313a1649ab 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.cleanupSome.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.cleanupSome.js
@@ -3,11 +3,12 @@ test("length is 0", () => {
});
function registerInDifferentScope(registry) {
- registry.register({}, {});
+ const target = {};
+ registry.register(target, {});
+ return target;
}
-// Flaky test, investigate and fix :^)
-test.skip("basic functionality", () => {
+test("basic functionality", () => {
var registry = new FinalizationRegistry(() => {});
var count = 0;
@@ -19,7 +20,8 @@ test.skip("basic functionality", () => {
expect(count).toBe(0);
- registerInDifferentScope(registry);
+ const target = registerInDifferentScope(registry);
+ markAsGarbage("target");
gc();
registry.cleanupSome(increment);
diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js
index f7e422da8e..9bc6afabbe 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js
@@ -21,10 +21,13 @@ test("invalid values", () => {
test("automatic removal of garbage-collected values", () => {
const weakMap = new WeakMap();
- {
- expect(weakMap.set({ a: 1 }, 1)).toBe(weakMap);
- expect(getWeakMapSize(weakMap)).toBe(1);
- }
+ const key = { e: 3 };
+
+ expect(weakMap.set(key, 1)).toBe(weakMap);
+ expect(getWeakMapSize(weakMap)).toBe(1);
+
+ markAsGarbage("key");
gc();
+
expect(getWeakMapSize(weakMap)).toBe(0);
});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.prototype.add.js b/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.prototype.add.js
index d9dd1eee2e..f9b40e0869 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.prototype.add.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.prototype.add.js
@@ -17,10 +17,13 @@ test("invalid values", () => {
test("automatic removal of garbage-collected values", () => {
const weakSet = new WeakSet();
- {
- expect(weakSet.add({ a: 1 })).toBe(weakSet);
- expect(getWeakSetSize(weakSet)).toBe(1);
- }
+ const item = { a: 1 };
+
+ expect(weakSet.add(item)).toBe(weakSet);
+ expect(getWeakSetSize(weakSet)).toBe(1);
+
+ markAsGarbage("item");
gc();
+
expect(getWeakSetSize(weakSet)).toBe(0);
});