mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 07:27:45 +00:00
LibJS: Add JS::SafeFunction, like Function but protects captures from GC
SafeFunction automatically registers its closure memory area in a place where the JS garbage collector can find it. This means that you can capture JS::Value and arbitrary pointers into the GC heap in closures, as long as you're using a SafeFunction, and the GC will not zap those values! There's probably some performance impact from this, and there's a lot of things that could be nicer/smarter about it, but let's build something that ensures safety first, and we can worry about performance later. :^)
This commit is contained in:
parent
585072fce3
commit
131c3f50de
3 changed files with 282 additions and 0 deletions
|
@ -17,6 +17,7 @@
|
|||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/WeakContainer.h>
|
||||
#include <LibJS/SafeFunction.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef __serenity__
|
||||
|
@ -29,6 +30,9 @@ namespace JS {
|
|||
static int gc_perf_string_id;
|
||||
#endif
|
||||
|
||||
// NOTE: We keep a per-thread list of custom ranges. This hinges on the assumption that there is one JS VM per thread.
|
||||
static __thread HashMap<FlatPtr*, size_t>* s_custom_ranges_for_conservative_scan = nullptr;
|
||||
|
||||
Heap::Heap(VM& vm)
|
||||
: m_vm(vm)
|
||||
{
|
||||
|
@ -164,6 +168,16 @@ __attribute__((no_sanitize("address"))) void Heap::gather_conservative_roots(Has
|
|||
add_possible_value(data);
|
||||
}
|
||||
|
||||
// NOTE: If we have any custom ranges registered, scan those as well.
|
||||
// This is where JS::SafeFunction closures get marked.
|
||||
if (s_custom_ranges_for_conservative_scan) {
|
||||
for (auto& custom_range : *s_custom_ranges_for_conservative_scan) {
|
||||
for (size_t i = 0; i < (custom_range.value / sizeof(FlatPtr)); ++i) {
|
||||
add_possible_value(custom_range.key[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HashTable<HeapBlock*> all_live_heap_blocks;
|
||||
for_each_block([&](auto& block) {
|
||||
all_live_heap_blocks.set(&block);
|
||||
|
@ -349,4 +363,21 @@ void Heap::uproot_cell(Cell* cell)
|
|||
m_uprooted_cells.append(cell);
|
||||
}
|
||||
|
||||
void register_safe_function_closure(void* base, size_t size)
|
||||
{
|
||||
if (!s_custom_ranges_for_conservative_scan) {
|
||||
// FIXME: This per-thread HashMap is currently leaked on thread exit.
|
||||
s_custom_ranges_for_conservative_scan = new HashMap<FlatPtr*, size_t>;
|
||||
}
|
||||
auto result = s_custom_ranges_for_conservative_scan->set(reinterpret_cast<FlatPtr*>(base), size);
|
||||
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
||||
}
|
||||
|
||||
void unregister_safe_function_closure(void* base, size_t)
|
||||
{
|
||||
VERIFY(s_custom_ranges_for_conservative_scan);
|
||||
bool did_remove = s_custom_ranges_for_conservative_scan->remove(reinterpret_cast<FlatPtr*>(base));
|
||||
VERIFY(did_remove);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue