From e5aa781f9e931d203784eafdf478b4baa4803e58 Mon Sep 17 00:00:00 2001 From: maurits seelen Date: Mon, 25 May 2026 19:10:44 +0200 Subject: [PATCH 1/9] Refactor for heap-based string handling and optimizations Refactored `NativeMemberDispatcher` and `VirtualMachine` to use `HeapManager` for heap-allocated strings and objects. Deprecated direct string handling in `Value.cs` and transitioned to heap-based methods. Added helper methods for heap management and string concatenation. Enabled parallel test execution in `MSTestSettings.cs`. Updated `Lumi.slnx` to adjust build configuration for tests. Added a TODO in `ConstantPool.cs` for dead constant removal. --- Lumi.Bytecode/Constants/ConstantPool.cs | 1 + Lumi.StdLib.Tests/MSTestSettings.cs | 1 + Lumi.VM/NativeMemberDispatcher.cs | 68 ++++++++++++------------- Lumi.VM/Value.cs | 5 +- Lumi.VM/VirtualMachine.cs | 55 +++++++++++++++----- Lumi.slnx | 4 +- 6 files changed, 82 insertions(+), 52 deletions(-) create mode 100644 Lumi.StdLib.Tests/MSTestSettings.cs diff --git a/Lumi.Bytecode/Constants/ConstantPool.cs b/Lumi.Bytecode/Constants/ConstantPool.cs index d1c24e9..fb9012a 100644 --- a/Lumi.Bytecode/Constants/ConstantPool.cs +++ b/Lumi.Bytecode/Constants/ConstantPool.cs @@ -7,6 +7,7 @@ /// internal sealed class ConstantPool { + // TODO: need a constantpool pass to remove dead constants after optimizations like constant folding and inlining. private readonly List _values = new(capacity: 16); private readonly Dictionary _numberIndex = []; private readonly Dictionary _stringIndex = new(StringComparer.Ordinal); diff --git a/Lumi.StdLib.Tests/MSTestSettings.cs b/Lumi.StdLib.Tests/MSTestSettings.cs new file mode 100644 index 0000000..300f5b1 --- /dev/null +++ b/Lumi.StdLib.Tests/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/Lumi.VM/NativeMemberDispatcher.cs b/Lumi.VM/NativeMemberDispatcher.cs index aa4e0a3..3997bac 100644 --- a/Lumi.VM/NativeMemberDispatcher.cs +++ b/Lumi.VM/NativeMemberDispatcher.cs @@ -61,13 +61,13 @@ private static Value InvokeFilePreludeMethod(HeapManager heap, string methodName { return methodName switch { - StdLibConstants.FilePreludeMethods.ReadText => Value.FromString(ReadAllText(methodName, args)), - StdLibConstants.FilePreludeMethods.WriteText => WriteText(args), - StdLibConstants.FilePreludeMethods.AppendText => AppendText(args), + StdLibConstants.FilePreludeMethods.ReadText => ReadText(heap, args), + StdLibConstants.FilePreludeMethods.WriteText => WriteText(heap, args), + StdLibConstants.FilePreludeMethods.AppendText => AppendText(heap, args), StdLibConstants.FilePreludeMethods.ReadLines => ReadLines(heap, args), StdLibConstants.FilePreludeMethods.WriteLines => WriteLines(heap, args), - StdLibConstants.FilePreludeMethods.Delete => Delete(args), - StdLibConstants.FilePreludeMethods.Create => Create(args), + StdLibConstants.FilePreludeMethods.Delete => Delete(heap, args), + StdLibConstants.FilePreludeMethods.Create => Create(heap, args), _ => throw VirtualMachineError.UnknownPreludeMethod(StandardLibraryRegistry.FilePreludeName, methodName) }; } @@ -77,29 +77,32 @@ private static Value InvokeFilePreludeMethod(HeapManager heap, string methodName } } - private static string ReadAllText(string methodName, IReadOnlyList args) - => File.ReadAllText(GetRequiredStringArgument(methodName, 0, args[0])); + private static string ReadAllText(HeapManager heap, string methodName, IReadOnlyList args) + => File.ReadAllText(GetRequiredStringArgument(methodName, 0, args[0], heap)); - private static Value Delete(IReadOnlyList args) + private static Value ReadText(HeapManager heap, IReadOnlyList args) + => Value.FromHeapObject(heap.InternString(ReadAllText(heap, StdLibConstants.FilePreludeMethods.ReadText, args))); + + private static Value Delete(HeapManager heap, IReadOnlyList args) { - var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.Delete, 0, args[0]); + var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.Delete, 0, args[0], heap); File.Delete(path); return Value.Undefined(); } - private static Value Create(IReadOnlyList args) + private static Value Create(HeapManager heap, IReadOnlyList args) { - var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.Create, 0, args[0]); + var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.Create, 0, args[0], heap); using var fileStream = File.Create(path); return Value.Undefined(); } - private static Value AppendText(IReadOnlyList args) + private static Value AppendText(HeapManager heap, IReadOnlyList args) { - var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.AppendText, 0, args[0]); - var contents = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.AppendText, 1, args[1]); + var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.AppendText, 0, args[0], heap); + var contents = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.AppendText, 1, args[1], heap); File.AppendAllText(path, contents); return Value.Undefined(); @@ -107,17 +110,14 @@ private static Value AppendText(IReadOnlyList args) private static Value WriteLines(HeapManager heap, IReadOnlyList args) { - var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteLines, 0, args[0]); + var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteLines, 0, args[0], heap); var linesArray = GetRequiredArrayArgument(StdLibConstants.FilePreludeMethods.WriteLines, 1, args[1], heap); var lines = new string[linesArray.Elements.Count]; for (var i = 0; i < linesArray.Elements.Count; i++) { var element = linesArray.Elements[i]; - if (element.Kind != ValueKind.String || element.String is null) - throw VirtualMachineError.MethodArgumentTypeMismatch(StdLibConstants.FilePreludeMethods.WriteLines, 1, ValueKind.String, element.Kind); - - lines[i] = element.String; + lines[i] = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteLines, 1, element, heap); } File.WriteAllLines(path, lines); @@ -139,17 +139,22 @@ private static Value RemoveArrayItem(HeapArrayObject target, Value item) return Value.FromBoolean(removed); } - private static Value WriteText(IReadOnlyList args) + private static Value WriteText(HeapManager heap, IReadOnlyList args) { - var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteText, 0, args[0]); - var contents = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteText, 1, args[1]); + var path = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteText, 0, args[0], heap); + var contents = GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.WriteText, 1, args[1], heap); File.WriteAllText(path, contents); return Value.Undefined(); } - private static string GetRequiredStringArgument(string methodName, int parameterIndex, Value value) + private static string GetRequiredStringArgument(string methodName, int parameterIndex, Value value, HeapManager? heap) { + if (heap is not null && value.IsHeapAllocated()) + { + return heap.GetStringValue(value.GetRequiredHeapHandle()); + } + if (value.Kind != ValueKind.String || value.String is null) throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.String, value.Kind); @@ -161,11 +166,7 @@ private static HeapArrayObject GetRequiredArrayArgument(string methodName, int p if (!value.IsHeapAllocated()) throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.Array, value.Kind); - var heapObject = heap.Get(value.GetRequiredHeapHandle()); - if (heapObject is not HeapArrayObject arrayObject) - throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.Array, heapObject.Kind); - - return arrayObject; + return heap.Get(value.GetRequiredHeapHandle()); } private static void ValidateArgumentCount(string methodName, int expected, int actual) @@ -176,16 +177,15 @@ private static void ValidateArgumentCount(string methodName, int expected, int a private static Value ReadLines(HeapManager heap, IReadOnlyList args) { - var lines = File.ReadAllLines(GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.ReadLines, 0, args[0])); + var lines = File.ReadAllLines(GetRequiredStringArgument(StdLibConstants.FilePreludeMethods.ReadLines, 0, args[0], heap)); var values = new List(lines.Length); foreach (var line in lines) { - values.Add(Value.FromString(line)); + var handle = heap.InternString(line); + values.Add(Value.FromHeapObject(handle)); } - var heapArray = new HeapArrayObject(values); - - return Value.FromHeapObject(heap.Allocate(heapArray)); + return Value.FromHeapObject(heap.Allocate(new HeapArrayObject(values))); } -} +} \ No newline at end of file diff --git a/Lumi.VM/Value.cs b/Lumi.VM/Value.cs index 81b55a7..18b9614 100644 --- a/Lumi.VM/Value.cs +++ b/Lumi.VM/Value.cs @@ -33,7 +33,7 @@ private Value( } public static Value FromNumber(double n) => new(ValueKind.Number, number: n); - public static Value FromString(string s) => new(ValueKind.String, str: s); // REMOVE after full implementation to heap-allocated strings + // public static Value FromString(string s) => new(ValueKind.String, str: s); // REMOVE after full implementation to heap-allocated strings public static Value FromBoolean(bool b) => new(ValueKind.Boolean, b: b); public static Value Undefined() => new(ValueKind.Undefined); public static Value FromHeapObject(HeapHandle heapHandle) => new(ValueKind.HeapObject, heapHandle: heapHandle); @@ -53,7 +53,7 @@ private Value( public static Value ConstantToValue(Constant constant) => constant.Kind switch { ConstantKind.Number => FromNumber(constant.Number), - ConstantKind.String => FromString(constant.String!), // TODO: we leave strings on the stack for now. Move to heap later. + // ConstantKind.String => FromString(constant.String!), // TODO: we leave strings on the stack for now. Move to heap later. ConstantKind.Boolean => FromBoolean(constant.Boolean), ConstantKind.Null => new(ValueKind.Null), ConstantKind.Undefined => new(ValueKind.Undefined), @@ -63,7 +63,6 @@ private Value( public string PrintValue() => Kind switch { ValueKind.Number => Number.ToString(), - ValueKind.String => "\"" + String + "\"" ?? string.Empty, // TODO: will be obsolete once we move string to the heap ValueKind.Boolean => Bool.ToString(), ValueKind.Null => "null", ValueKind.Undefined => "undefined", diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index 592899f..2f20d11 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -52,7 +52,7 @@ public void Execute(BytecodeResult bytecode) switch (instruction.Kind) { case InstructionKind.PushConst: - _stack[_stackTop++] = Value.ConstantToValue(constants[instruction.IntOperand.GetValueOrDefault()]); + _stack[_stackTop++] = ConstantToValue(constants[instruction.IntOperand.GetValueOrDefault()]); break; case InstructionKind.Add: @@ -60,10 +60,10 @@ public void Execute(BytecodeResult bytecode) var b = _stack[--_stackTop]; var a = _stack[--_stackTop]; - // NOTE: clean this up. - if (a.Kind == ValueKind.String || b.Kind == ValueKind.String) + if (TryGetStringValue(a, out _) || TryGetStringValue(b, out _)) { - _stack[_stackTop++] = Value.FromString(a.ToString() + b.ToString()); + var handle = _heap.InternString(StringifyForConcatenation(a) + StringifyForConcatenation(b)); + _stack[_stackTop++] = Value.FromHeapObject(handle); break; } _stack[_stackTop++] = Value.FromNumber(a.Number + b.Number); @@ -115,9 +115,7 @@ public void Execute(BytecodeResult bytecode) case InstructionKind.LoadPreludeGlobal: { - var heapObject = new HeapNativeObject(instruction.GetSafeStringOperand(), []); // TODO: fields? - _heap.MaybeCollect(EnumerateRoots()); - _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); + AllocateHeapObject(new HeapNativeObject(instruction.GetSafeStringOperand(), [])); break; } @@ -144,9 +142,7 @@ public void Execute(BytecodeResult bytecode) values[fields[i]] = i < args.Length ? args[i] : Value.Undefined(); } - var heapObject = new HeapStructObject(structName, values); - _heap.MaybeCollect(EnumerateRoots()); - _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); + AllocateHeapObject(new HeapStructObject(structName, values)); break; } @@ -203,9 +199,7 @@ public void Execute(BytecodeResult bytecode) values[i] = _stack[--_stackTop]; } - var heapObject = new HeapArrayObject([.. values]); - _heap.MaybeCollect(EnumerateRoots()); - _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); + AllocateHeapObject(new HeapArrayObject([.. values])); break; } @@ -429,4 +423,37 @@ private IEnumerable EnumerateRoots() yield return _variables[i]; } } -} + + private void AllocateHeapObject(HeapObject heapObject) + { + _heap.MaybeCollect(EnumerateRoots()); + _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); + } + + private Value ConstantToValue(Constant constant) => constant.Kind switch + { + ConstantKind.String => Value.FromHeapObject(_heap.InternString(constant.String!)), + _ => Value.ConstantToValue(constant), + }; + + private bool TryGetStringValue(Value value, out string text) + { + text = string.Empty; + + if (!value.IsHeapAllocated()) + { + return false; + } + + text = _heap.GetStringValue(value.GetRequiredHeapHandle()); + + return true; + } + + private string StringifyForConcatenation(Value value) + => TryGetStringValue(value, out var text) + ? text + : value.IsHeapAllocated() + ? _heap.GetStringValue(value.GetRequiredHeapHandle()) + : value.PrintValue(); +} \ No newline at end of file diff --git a/Lumi.slnx b/Lumi.slnx index bfbdee6..a54197b 100644 --- a/Lumi.slnx +++ b/Lumi.slnx @@ -35,7 +35,9 @@ - + + + From 80ecf50f5187b50b3b5eebbf6081265ab322eaf7 Mon Sep 17 00:00:00 2001 From: maurits seelen Date: Mon, 25 May 2026 19:24:35 +0200 Subject: [PATCH 2/9] Refactor Value to use heap-allocated strings Removed the `String` property and related methods from the `Value` struct to transition to heap-allocated strings. Updated `NativeMemberDispatcher` to handle this change and throw appropriate errors for mismatched value kinds. Adjusted `ConstantToValue` to reflect the removal of direct string handling. --- Lumi.VM/NativeMemberDispatcher.cs | 5 +---- Lumi.VM/Value.cs | 5 ----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Lumi.VM/NativeMemberDispatcher.cs b/Lumi.VM/NativeMemberDispatcher.cs index 3997bac..dd6bf24 100644 --- a/Lumi.VM/NativeMemberDispatcher.cs +++ b/Lumi.VM/NativeMemberDispatcher.cs @@ -155,10 +155,7 @@ private static string GetRequiredStringArgument(string methodName, int parameter return heap.GetStringValue(value.GetRequiredHeapHandle()); } - if (value.Kind != ValueKind.String || value.String is null) - throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.String, value.Kind); - - return value.String; + throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.String, value.Kind); } private static HeapArrayObject GetRequiredArrayArgument(string methodName, int parameterIndex, Value value, HeapManager heap) diff --git a/Lumi.VM/Value.cs b/Lumi.VM/Value.cs index 18b9614..d225dff 100644 --- a/Lumi.VM/Value.cs +++ b/Lumi.VM/Value.cs @@ -14,26 +14,22 @@ internal readonly struct Value // Payload fields — only the one matching Kind is populated. public double Number { get; } - public string? String { get; } public bool Bool { get; } public HeapHandle? HeapHandle { get; } private Value( ValueKind kind, double number = 0, - string? str = null, bool b = false, HeapHandle? heapHandle = null) { Kind = kind; Number = number; - String = str; Bool = b; HeapHandle = heapHandle; } public static Value FromNumber(double n) => new(ValueKind.Number, number: n); - // public static Value FromString(string s) => new(ValueKind.String, str: s); // REMOVE after full implementation to heap-allocated strings public static Value FromBoolean(bool b) => new(ValueKind.Boolean, b: b); public static Value Undefined() => new(ValueKind.Undefined); public static Value FromHeapObject(HeapHandle heapHandle) => new(ValueKind.HeapObject, heapHandle: heapHandle); @@ -53,7 +49,6 @@ private Value( public static Value ConstantToValue(Constant constant) => constant.Kind switch { ConstantKind.Number => FromNumber(constant.Number), - // ConstantKind.String => FromString(constant.String!), // TODO: we leave strings on the stack for now. Move to heap later. ConstantKind.Boolean => FromBoolean(constant.Boolean), ConstantKind.Null => new(ValueKind.Null), ConstantKind.Undefined => new(ValueKind.Undefined), From dc911f2b8f995a0de3dcdfacbc03c834645df412 Mon Sep 17 00:00:00 2001 From: maurits seelen Date: Mon, 25 May 2026 19:28:56 +0200 Subject: [PATCH 3/9] Undo --- Lumi.slnx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lumi.slnx b/Lumi.slnx index a54197b..8968df1 100644 --- a/Lumi.slnx +++ b/Lumi.slnx @@ -35,9 +35,7 @@ - - - + From cf23659dd213bfcbb365d0dc3946ee67789e47a0 Mon Sep 17 00:00:00 2001 From: Maurits Seelen Date: Mon, 25 May 2026 19:37:27 +0200 Subject: [PATCH 4/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Lumi.VM/VirtualMachine.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index 2f20d11..37d87d0 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -424,9 +424,18 @@ private IEnumerable EnumerateRoots() } } - private void AllocateHeapObject(HeapObject heapObject) + private void AllocateHeapObject(HeapObject heapObject, IEnumerable? additionalRoots = null) { - _heap.MaybeCollect(EnumerateRoots()); + IEnumerable Roots() + { + foreach (var root in EnumerateRoots()) yield return root; + if (additionalRoots is not null) + { + foreach (var root in additionalRoots) yield return root; + } + } + + _heap.MaybeCollect(Roots()); _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); } From 9a59919d2215f9dfe1e2840545aa9c4724a940b6 Mon Sep 17 00:00:00 2001 From: Maurits Seelen Date: Mon, 25 May 2026 19:37:36 +0200 Subject: [PATCH 5/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Lumi.VM/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index 37d87d0..7b20dff 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -142,7 +142,7 @@ public void Execute(BytecodeResult bytecode) values[fields[i]] = i < args.Length ? args[i] : Value.Undefined(); } - AllocateHeapObject(new HeapStructObject(structName, values)); + AllocateHeapObject(new HeapStructObject(structName, values), values.Values); break; } From 94355ed31d45b6c135f0944d1f685f3a0fc6dfa7 Mon Sep 17 00:00:00 2001 From: Maurits Seelen Date: Mon, 25 May 2026 19:37:42 +0200 Subject: [PATCH 6/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Lumi.VM/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index 7b20dff..6534c0e 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -199,7 +199,7 @@ public void Execute(BytecodeResult bytecode) values[i] = _stack[--_stackTop]; } - AllocateHeapObject(new HeapArrayObject([.. values])); + AllocateHeapObject(new HeapArrayObject([.. values]), values); break; } From b45cda8159ed420193563719aa9377afe6ca2f80 Mon Sep 17 00:00:00 2001 From: Maurits Seelen Date: Mon, 25 May 2026 19:38:01 +0200 Subject: [PATCH 7/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Lumi.VM/VirtualMachine.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index 6534c0e..d2e9158 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -454,15 +454,18 @@ private bool TryGetStringValue(Value value, out string text) return false; } - text = _heap.GetStringValue(value.GetRequiredHeapHandle()); + var obj = _heap.Get(value.GetRequiredHeapHandle()); + if (obj is not HeapStringObject stringObj) + { + return false; + } + text = stringObj.Value; return true; } private string StringifyForConcatenation(Value value) => TryGetStringValue(value, out var text) ? text - : value.IsHeapAllocated() - ? _heap.GetStringValue(value.GetRequiredHeapHandle()) - : value.PrintValue(); + : _heap.FormatValue(value); } \ No newline at end of file From a0dc6d5fd97585ff9eb77a0a5325bf0a819ecb15 Mon Sep 17 00:00:00 2001 From: Maurits Seelen Date: Mon, 25 May 2026 19:39:09 +0200 Subject: [PATCH 8/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Lumi.VM/VirtualMachine.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index d2e9158..7d0109b 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -439,11 +439,16 @@ IEnumerable Roots() _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); } - private Value ConstantToValue(Constant constant) => constant.Kind switch + private Value ConstantToValue(Constant constant) { - ConstantKind.String => Value.FromHeapObject(_heap.InternString(constant.String!)), - _ => Value.ConstantToValue(constant), - }; + if (constant.Kind != ConstantKind.String) + { + return Value.ConstantToValue(constant); + } + + _heap.MaybeCollect(EnumerateRoots()); + return Value.FromHeapObject(_heap.InternString(constant.String!)); + } private bool TryGetStringValue(Value value, out string text) { From af9dd67d80f96411d850d9a2eed451b105aee2d6 Mon Sep 17 00:00:00 2001 From: maurits seelen Date: Mon, 25 May 2026 19:46:14 +0200 Subject: [PATCH 9/9] Refactor heap root handling and improve GC calls Extracted root enumeration to a Roots method for reuse. Updated string concatenation and heap allocation to use precise roots during garbage collection, improving clarity and maintainability. --- Lumi.VM/VirtualMachine.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Lumi.VM/VirtualMachine.cs b/Lumi.VM/VirtualMachine.cs index 7d0109b..c41d05a 100644 --- a/Lumi.VM/VirtualMachine.cs +++ b/Lumi.VM/VirtualMachine.cs @@ -62,6 +62,7 @@ public void Execute(BytecodeResult bytecode) if (TryGetStringValue(a, out _) || TryGetStringValue(b, out _)) { + _heap.MaybeCollect(Roots([a, b])); var handle = _heap.InternString(StringifyForConcatenation(a) + StringifyForConcatenation(b)); _stack[_stackTop++] = Value.FromHeapObject(handle); break; @@ -424,17 +425,17 @@ private IEnumerable EnumerateRoots() } } - private void AllocateHeapObject(HeapObject heapObject, IEnumerable? additionalRoots = null) + private IEnumerable Roots(IEnumerable? additionalRoots = null) { - IEnumerable Roots() + foreach (var root in EnumerateRoots()) yield return root; + if (additionalRoots is not null) { - foreach (var root in EnumerateRoots()) yield return root; - if (additionalRoots is not null) - { - foreach (var root in additionalRoots) yield return root; - } + foreach (var root in additionalRoots) yield return root; } + } + private void AllocateHeapObject(HeapObject heapObject, IEnumerable? additionalRoots = null) + { _heap.MaybeCollect(Roots()); _stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject)); }