Skip to content

Commit c15c5da

Browse files
authored
Merge pull request #21 from Ovum-Programming-Language/garbage_collector
Garbage collector 🫣
2 parents 3d130e3 + 0a1d7ee commit c15c5da

46 files changed

Lines changed: 1260 additions & 378 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/bytecode_parser/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ add_library(bytecode_parser STATIC
99
scenarios/VtableParser.cpp
1010
scenarios/CommandFactory.cpp
1111
scenarios/FunctionFactory.cpp
12-
scenarios/PlaceholderCommandFactory.cpp
1312
)
1413

1514

lib/bytecode_parser/ParsingSession.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "ParsingSession.hpp"
22

33
#include <tokens/EofToken.hpp>
4+
#include <tokens/LiteralToken.hpp>
5+
#include <tokens/values/StringValue.hpp>
46

57
namespace ovum::bytecode::parser {
68

@@ -118,7 +120,8 @@ std::expected<std::string, BytecodeParserError> ParsingSession::ConsumeStringLit
118120
std::to_string(token->GetPosition().GetColumn())));
119121
}
120122

121-
std::string value = Current()->GetLexeme();
123+
std::string value =
124+
dynamic_cast<StringValue*>(dynamic_cast<ovum::LiteralToken*>(Current().get())->GetValue())->ToString();
122125

123126
value = value.substr(1, value.length() - 2);
124127

lib/bytecode_parser/scenarios/CommandFactory.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
namespace ovum::bytecode::parser {
88

9-
const std::unordered_set<std::string> CommandFactory::kStringCommands = {"PushString", "PushChar"};
9+
const std::unordered_set<std::string> CommandFactory::kStringCommands = {"PushString"};
10+
const std::unordered_set<std::string> CommandFactory::kCharCommands = {"PushChar"};
1011

1112
const std::unordered_set<std::string> CommandFactory::kIntegerCommands = {
1213
"PushInt", "PushByte", "Rotate", "LoadLocal", "SetLocal", "LoadStatic", "SetStatic", "GetField", "SetField"};
@@ -16,7 +17,7 @@ const std::unordered_set<std::string> CommandFactory::kFloatCommands = {"PushFlo
1617
const std::unordered_set<std::string> CommandFactory::kBooleanCommands = {"PushBool"};
1718

1819
const std::unordered_set<std::string> CommandFactory::kIdentCommands = {
19-
"NewArray", "Call", "CallVirtual", "CallConstructor", "GetVTable", "SetVTable", "SafeCall", "IsType", "SizeOf"};
20+
"Call", "CallVirtual", "CallConstructor", "GetVTable", "SetVTable", "SafeCall", "IsType", "SizeOf"};
2021

2122
std::expected<std::unique_ptr<vm::execution_tree::IExecutable>, BytecodeParserError> CommandFactory::CreateCommand(
2223
const std::string& cmd_name, std::shared_ptr<ParsingSession> ctx) const {
@@ -105,6 +106,23 @@ std::expected<std::unique_ptr<vm::execution_tree::IExecutable>, BytecodeParserEr
105106
return std::move(cmd.value());
106107
}
107108

109+
if (kCharCommands.contains(cmd_name)) {
110+
std::expected<int64_t, BytecodeParserError> value = ctx->ConsumeIntLiteral();
111+
112+
if (!value) {
113+
return std::unexpected(value.error());
114+
}
115+
116+
std::expected<std::unique_ptr<vm::execution_tree::IExecutable>, std::out_of_range> cmd =
117+
vm::execution_tree::CreateIntegerCommandByName(cmd_name, value.value());
118+
119+
if (!cmd) {
120+
return std::unexpected(BytecodeParserError("Failed to create char command: " + cmd_name));
121+
}
122+
123+
return std::move(cmd.value());
124+
}
125+
108126
std::expected<std::unique_ptr<vm::execution_tree::IExecutable>, std::out_of_range> cmd =
109127
vm::execution_tree::CreateSimpleCommandByName(cmd_name);
110128

lib/bytecode_parser/scenarios/CommandFactory.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class CommandFactory : public ICommandFactory {
2727
static const std::unordered_set<std::string> kFloatCommands;
2828
static const std::unordered_set<std::string> kBooleanCommands;
2929
static const std::unordered_set<std::string> kIdentCommands;
30+
static const std::unordered_set<std::string> kCharCommands;
3031
};
3132

3233
} // namespace ovum::bytecode::parser

lib/bytecode_parser/scenarios/PlaceholderCommandFactory.cpp

Lines changed: 0 additions & 79 deletions
This file was deleted.

lib/bytecode_parser/scenarios/PlaceholderCommandFactory.hpp

Lines changed: 0 additions & 21 deletions
This file was deleted.

lib/execution_tree/BytecodeCommands.cpp

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include <cmath>
66
#include <ctime>
77
#include <filesystem>
8-
#include <iostream>
98
#include <random>
109
#include <ranges>
1110
#include <sstream>
@@ -21,8 +20,17 @@
2120

2221
#ifdef _WIN32
2322
#include <windows.h>
23+
24+
#include <psapi.h>
25+
#elif __APPLE__
26+
#include <dlfcn.h>
27+
#include <unistd.h>
28+
29+
#include <mach/mach.h>
30+
#include <mach/task_info.h>
2431
#else
2532
#include <dlfcn.h>
33+
#include <sys/resource.h>
2634
#include <unistd.h>
2735
#endif
2836

@@ -109,10 +117,8 @@ std::expected<ExecutionResult, std::runtime_error> PushString(PassedExecutionDat
109117
return std::unexpected(vtable_index_result.error());
110118
}
111119

112-
auto string_obj_result = runtime::AllocateObject(*string_vtable,
113-
static_cast<uint32_t>(vtable_index_result.value()),
114-
data.memory.object_repository,
115-
data.allocator);
120+
auto string_obj_result =
121+
data.memory_manager.AllocateObject(*string_vtable, static_cast<uint32_t>(vtable_index_result.value()), data);
116122

117123
if (!string_obj_result.has_value()) {
118124
return std::unexpected(string_obj_result.error());
@@ -140,8 +146,8 @@ std::expected<ExecutionResult, std::runtime_error> PushNull(PassedExecutionData&
140146
return std::unexpected(vtable_index_result.error());
141147
}
142148

143-
auto null_obj_result = runtime::AllocateObject(
144-
*null_vtable, static_cast<uint32_t>(vtable_index_result.value()), data.memory.object_repository, data.allocator);
149+
auto null_obj_result =
150+
data.memory_manager.AllocateObject(*null_vtable, static_cast<uint32_t>(vtable_index_result.value()), data);
145151

146152
if (!null_obj_result.has_value()) {
147153
return std::unexpected(null_obj_result.error());
@@ -1211,8 +1217,7 @@ std::expected<ExecutionResult, std::runtime_error> CallConstructor(PassedExecuti
12111217
return std::unexpected(vtable.error());
12121218
}
12131219

1214-
auto obj_ptr =
1215-
runtime::AllocateObject(*vtable.value(), vtable_idx.value(), data.memory.object_repository, data.allocator);
1220+
auto obj_ptr = data.memory_manager.AllocateObject(*vtable.value(), vtable_idx.value(), data);
12161221

12171222
if (!obj_ptr) {
12181223
return std::unexpected(obj_ptr.error());
@@ -1452,7 +1457,7 @@ std::expected<ExecutionResult, std::runtime_error> NullCoalesce(PassedExecutionD
14521457

14531458
if (*tested_result_data != nullptr) {
14541459
data.memory.machine_stack.pop();
1455-
data.memory.machine_stack.emplace(tested_result.value());
1460+
data.memory.machine_stack.emplace(*tested_result_data);
14561461
}
14571462

14581463
return ExecutionResult::kNormal;
@@ -1602,10 +1607,9 @@ std::expected<ExecutionResult, std::runtime_error> FormatDateTime(PassedExecutio
16021607
return std::unexpected(vtable_index_result.error());
16031608
}
16041609

1605-
auto string_obj_result = runtime::AllocateObject(*string_vtable,
1606-
static_cast<uint32_t>(vtable_index_result.value()),
1607-
data.memory.object_repository,
1608-
data.allocator);
1610+
auto string_obj_result =
1611+
data.memory_manager.AllocateObject(*string_vtable, static_cast<uint32_t>(vtable_index_result.value()), data);
1612+
16091613
if (!string_obj_result.has_value()) {
16101614
return std::unexpected(string_obj_result.error());
16111615
}
@@ -1659,8 +1663,9 @@ std::expected<ExecutionResult, std::runtime_error> ParseDateTime(PassedExecution
16591663
return std::unexpected(vtable_index_result.error());
16601664
}
16611665

1662-
auto int_obj_result = runtime::AllocateObject(
1663-
*int_vtable, static_cast<uint32_t>(vtable_index_result.value()), data.memory.object_repository, data.allocator);
1666+
auto int_obj_result =
1667+
data.memory_manager.AllocateObject(*int_vtable, static_cast<uint32_t>(vtable_index_result.value()), data);
1668+
16641669
if (!int_obj_result.has_value()) {
16651670
return std::unexpected(int_obj_result.error());
16661671
}
@@ -1821,10 +1826,8 @@ std::expected<ExecutionResult, std::runtime_error> ListDir(PassedExecutionData&
18211826
return std::unexpected(vtable_index_result.error());
18221827
}
18231828

1824-
auto string_array_obj_result = runtime::AllocateObject(*string_array_vtable,
1825-
static_cast<uint32_t>(vtable_index_result.value()),
1826-
data.memory.object_repository,
1827-
data.allocator);
1829+
auto string_array_obj_result = data.memory_manager.AllocateObject(
1830+
*string_array_vtable, static_cast<uint32_t>(vtable_index_result.value()), data);
18281831
if (!string_array_obj_result.has_value()) {
18291832
return std::unexpected(string_array_obj_result.error());
18301833
}
@@ -1847,10 +1850,9 @@ std::expected<ExecutionResult, std::runtime_error> ListDir(PassedExecutionData&
18471850
return std::unexpected(string_vtable_index_result.error());
18481851
}
18491852

1850-
auto string_obj_result = runtime::AllocateObject(*string_vtable,
1851-
static_cast<uint32_t>(string_vtable_index_result.value()),
1852-
data.memory.object_repository,
1853-
data.allocator);
1853+
auto string_obj_result = data.memory_manager.AllocateObject(
1854+
*string_vtable, static_cast<uint32_t>(string_vtable_index_result.value()), data);
1855+
18541856
if (!string_obj_result.has_value()) {
18551857
return std::unexpected(string_obj_result.error());
18561858
}
@@ -2068,20 +2070,36 @@ std::expected<ExecutionResult, std::runtime_error> SeedRandom(PassedExecutionDat
20682070
}
20692071

20702072
std::expected<ExecutionResult, std::runtime_error> GetMemoryUsage(PassedExecutionData& data) {
2071-
// Simple memory usage approximation
20722073
size_t memory_usage = 0;
20732074

2074-
// Stack size
2075-
memory_usage += data.memory.machine_stack.size() * sizeof(runtime::Variable);
2076-
2077-
auto stack_copy = data.memory.stack_frames;
2078-
// Local variables in all stack frames
2079-
while (!stack_copy.empty()) {
2080-
memory_usage += stack_copy.top().local_variables.size() * sizeof(runtime::Variable);
2081-
stack_copy.pop();
2075+
#ifdef _WIN32
2076+
PROCESS_MEMORY_COUNTERS pmc;
2077+
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
2078+
memory_usage = static_cast<size_t>(pmc.WorkingSetSize);
2079+
} else {
2080+
return std::unexpected(std::runtime_error("GetMemoryUsage: failed to get process memory info"));
2081+
}
2082+
#elif __APPLE__
2083+
struct task_basic_info info {};
2084+
mach_msg_type_number_t size = sizeof(info);
2085+
kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&info), &size);
2086+
if (kerr == KERN_SUCCESS) {
2087+
memory_usage = static_cast<size_t>(info.resident_size);
2088+
} else {
2089+
return std::unexpected(std::runtime_error("GetMemoryUsage: failed to get task memory info"));
2090+
}
2091+
#else
2092+
// Linux: Use getrusage() system call
2093+
struct rusage usage {};
2094+
constexpr size_t kBytesInRusageUnit = 1024;
2095+
if (getrusage(RUSAGE_SELF, &usage) != 0) {
2096+
return std::unexpected(std::runtime_error("GetMemoryUsage: getrusage() failed"));
20822097
}
20832098

2084-
// TODO counter for repositories
2099+
// ru_maxrss is the maximum resident set size in kilobytes
2100+
// Note: getrusage gives maximum usage, not current usage
2101+
memory_usage = static_cast<size_t>(usage.ru_maxrss) * kBytesInRusageUnit;
2102+
#endif
20852103

20862104
data.memory.machine_stack.emplace(static_cast<int64_t>(memory_usage));
20872105
return ExecutionResult::kNormal;
@@ -2094,8 +2112,10 @@ std::expected<ExecutionResult, std::runtime_error> GetPeakMemoryUsage(PassedExec
20942112

20952113
std::expected<ExecutionResult, std::runtime_error> ForceGarbageCollection(PassedExecutionData& data) {
20962114
// Simple garbage collection: remove unreachable objects
2097-
// This is a placeholder implementation
2098-
// data.memory.object_repository.CollectGarbage();
2115+
auto result = data.memory_manager.CollectGarbage(data);
2116+
if (!result.has_value()) {
2117+
return std::unexpected(result.error());
2118+
}
20992119
return ExecutionResult::kNormal;
21002120
}
21012121

lib/execution_tree/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ add_library(execution_tree STATIC
1313
target_include_directories(execution_tree PUBLIC ${PROJECT_SOURCE_DIR})
1414

1515
target_link_libraries(execution_tree PUBLIC runtime)
16+
17+
if(WIN32)
18+
target_link_libraries(execution_tree PRIVATE psapi)
19+
endif()

lib/execution_tree/Command.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ class Command : public IExecutable {
3535
return std::unexpected(std::runtime_error(error_message));
3636
}
3737

38+
auto gc_res = execution_data.memory_manager.CollectGarbageIfRequired(execution_data);
39+
40+
if (!gc_res) {
41+
return std::unexpected(gc_res.error());
42+
}
43+
3844
return result.value();
3945
}
4046

lib/execution_tree/PassedExecutionData.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <memory>
66
#include <ostream>
77

8+
#include "lib/runtime/MemoryManager.hpp"
89
#include "lib/runtime/RuntimeMemory.hpp"
910
#include "lib/runtime/VirtualTableRepository.hpp"
1011

@@ -16,7 +17,7 @@ struct PassedExecutionData {
1617
runtime::RuntimeMemory& memory;
1718
const runtime::VirtualTableRepository& virtual_table_repository;
1819
const FunctionRepository& function_repository;
19-
std::allocator<char> allocator;
20+
runtime::MemoryManager& memory_manager;
2021
std::istream& input_stream;
2122
std::ostream& output_stream;
2223
std::ostream& error_stream;

0 commit comments

Comments
 (0)