ArkScript
A small, lisp-inspired, functional scripting language
Closure.cpp
Go to the documentation of this file.
2
4#include <Ark/VM/VM.hpp>
5
6#include <ranges>
7
8namespace Ark::internal
9{
10 // This can cause a memory leak if a closure is referencing itself.
11 // However, I don't think this is worthy to try and fix, I spent far
12 // too much time (~10 days, 4 different solutions) and energy on it,
13 // and no solution was good enough, they all had pretty big flaws:
14 // 1. keeping the closures in a central place and reference them using
15 // raw pointers : need GC or have a growing memory that will be
16 // fred only at the end).
17 // 2. use a generational storage, one scope = one generation, free old
18 // gens when we create a new one. Alas we can create a closure in a
19 // gen and return it in another, so it would have created dangling refs.
20 // 3. use a variant weak/shared and copy as weak ptr, the leak is still
21 // there.
22 // 4. like 1, put all shared ptrs in the execution context and use weak ref,
23 // it needs a GC or the memory will grow until the execution context is
24 // deleted.
25 // This problem is being ignored by the CI, via lsan-suppressions.txt and
26 // valgrind-suppressions.txt.
27 Closure::Closure(const ClosureScope& scope, const PageAddr_t pa) noexcept :
28 m_scope(std::make_shared<ClosureScope>(scope)),
29 m_page_addr(pa)
30 {}
31
32 Closure::Closure(const std::shared_ptr<ClosureScope>& scope_ptr, const PageAddr_t pa) noexcept :
33 m_scope(scope_ptr),
34 m_page_addr(pa)
35 {}
36
37 bool Closure::hasFieldEndingWith(const std::string& end, const VM& vm) const
38 {
39 return std::ranges::any_of(std::ranges::views::keys(m_scope->m_data), [&vm, &end](const auto& id) {
40 return end.ends_with(":" + vm.m_state.m_symbols[id]);
41 });
42 }
43
44 std::string Closure::toString(VM& vm) const noexcept
45 {
46 std::string out = "(";
47 for (std::size_t i = 0, end = m_scope->m_data.size(); i < end; ++i)
48 {
49 const auto& [id, value] = m_scope->m_data[i];
50 if (i != 0)
51 out += ' ';
52
53 out += '.' + vm.m_state.m_symbols[id] + '=';
54 if (value.valueType() == ValueType::Closure && value.closure().scopePtr() == scopePtr())
55 out += "Ref(self)";
56 else
57 out += value.toString(vm);
58 }
59 return out + ")";
60 }
61
62 bool operator==(const Closure& A, const Closure& B) noexcept
63 {
64 // they do not come from the same closure builder
65 if (A.m_page_addr != B.m_page_addr)
66 return false;
67 // pointers are identical, we are dealing with the same object
68 if (A.m_scope.get() == B.m_scope.get())
69 return true;
70
71 return *A.m_scope == *B.m_scope;
72 }
73}
Subtype of the value type, handling closures.
Subtype of the value type, handling closures.
The ArkScript virtual machine.
The ArkScript virtual machine, executing ArkScript bytecode.
Definition VM.hpp:40
A class to store fields captured by a closure.
Closure management.
Definition Closure.hpp:36
std::string toString(VM &vm) const noexcept
Print the closure to a string.
Definition Closure.cpp:44
bool hasFieldEndingWith(const std::string &end, const VM &vm) const
Used when generating error messages in the VM, to see if a symbol might have been wrongly fully quali...
Definition Closure.cpp:37
std::shared_ptr< ClosureScope > m_scope
Definition Closure.hpp:84
Closure(const ClosureScope &scope, PageAddr_t pa) noexcept
Construct a new Closure object.
Definition Closure.cpp:27
bool operator==(const Namespace &A, const Namespace &B)
Definition Namespace.hpp:21
uint16_t PageAddr_t
Definition Closure.hpp:27