Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Lumi.Bytecode/Constants/ConstantPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/// </summary>
internal sealed class ConstantPool
{
// TODO: need a constantpool pass to remove dead constants after optimizations like constant folding and inlining.
private readonly List<Constant> _values = new(capacity: 16);
private readonly Dictionary<double, int> _numberIndex = [];
private readonly Dictionary<string, int> _stringIndex = new(StringComparer.Ordinal);
Expand Down
1 change: 1 addition & 0 deletions Lumi.StdLib.Tests/MSTestSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
71 changes: 34 additions & 37 deletions Lumi.VM/NativeMemberDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};
}
Expand All @@ -77,47 +77,47 @@ private static Value InvokeFilePreludeMethod(HeapManager heap, string methodName
}
}

private static string ReadAllText(string methodName, IReadOnlyList<Value> args)
=> File.ReadAllText(GetRequiredStringArgument(methodName, 0, args[0]));
private static string ReadAllText(HeapManager heap, string methodName, IReadOnlyList<Value> args)
=> File.ReadAllText(GetRequiredStringArgument(methodName, 0, args[0], heap));

private static Value Delete(IReadOnlyList<Value> args)
private static Value ReadText(HeapManager heap, IReadOnlyList<Value> args)
=> Value.FromHeapObject(heap.InternString(ReadAllText(heap, StdLibConstants.FilePreludeMethods.ReadText, args)));

private static Value Delete(HeapManager heap, IReadOnlyList<Value> 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<Value> args)
private static Value Create(HeapManager heap, IReadOnlyList<Value> 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<Value> args)
private static Value AppendText(HeapManager heap, IReadOnlyList<Value> 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();
}

private static Value WriteLines(HeapManager heap, IReadOnlyList<Value> 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);
Expand All @@ -139,33 +139,31 @@ private static Value RemoveArrayItem(HeapArrayObject target, Value item)
return Value.FromBoolean(removed);
}

private static Value WriteText(IReadOnlyList<Value> args)
private static Value WriteText(HeapManager heap, IReadOnlyList<Value> 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 (value.Kind != ValueKind.String || value.String is null)
throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.String, value.Kind);
if (heap is not null && value.IsHeapAllocated())
{
return heap.GetStringValue(value.GetRequiredHeapHandle());
}

Comment thread
Mauw94 marked this conversation as resolved.
return value.String;
throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.String, value.Kind);
}

private static HeapArrayObject GetRequiredArrayArgument(string methodName, int parameterIndex, Value value, HeapManager heap)
{
if (!value.IsHeapAllocated())
throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.Array, value.Kind);

var heapObject = heap.Get<HeapObject>(value.GetRequiredHeapHandle());
if (heapObject is not HeapArrayObject arrayObject)
throw VirtualMachineError.MethodArgumentTypeMismatch(methodName, parameterIndex, ValueKind.Array, heapObject.Kind);

return arrayObject;
return heap.Get<HeapArrayObject>(value.GetRequiredHeapHandle());
}

private static void ValidateArgumentCount(string methodName, int expected, int actual)
Expand All @@ -176,16 +174,15 @@ private static void ValidateArgumentCount(string methodName, int expected, int a

private static Value ReadLines(HeapManager heap, IReadOnlyList<Value> 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<Value>(lines.Length);

foreach (var line in lines)
{
values.Add(Value.FromString(line));
var handle = heap.InternString(line);
values.Add(Value.FromHeapObject(handle));
}
Comment thread
Mauw94 marked this conversation as resolved.

var heapArray = new HeapArrayObject(values);

return Value.FromHeapObject(heap.Allocate(heapArray));
return Value.FromHeapObject(heap.Allocate(new HeapArrayObject(values)));
}
}
}
6 changes: 0 additions & 6 deletions Lumi.VM/Value.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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),
Expand All @@ -63,7 +58,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",
Expand Down
73 changes: 59 additions & 14 deletions Lumi.VM/VirtualMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,19 @@ 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:
{
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());
_heap.MaybeCollect(Roots([a, b]));
var handle = _heap.InternString(StringifyForConcatenation(a) + StringifyForConcatenation(b));
_stack[_stackTop++] = Value.FromHeapObject(handle);
break;
Comment thread
Mauw94 marked this conversation as resolved.
}
_stack[_stackTop++] = Value.FromNumber(a.Number + b.Number);
Expand Down Expand Up @@ -115,9 +116,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;
}

Expand All @@ -144,9 +143,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), values.Values);
break;
}

Expand Down Expand Up @@ -203,9 +200,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]), values);
break;
}

Expand Down Expand Up @@ -429,4 +424,54 @@ private IEnumerable<Value> EnumerateRoots()
yield return _variables[i];
}
}
}

private IEnumerable<Value> Roots(IEnumerable<Value>? additionalRoots = null)
{
foreach (var root in EnumerateRoots()) yield return root;
if (additionalRoots is not null)
{
foreach (var root in additionalRoots) yield return root;
}
}

private void AllocateHeapObject(HeapObject heapObject, IEnumerable<Value>? additionalRoots = null)
{
_heap.MaybeCollect(Roots());
_stack[_stackTop++] = Value.FromHeapObject(_heap.Allocate(heapObject));
}

private Value ConstantToValue(Constant 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)
{
text = string.Empty;

if (!value.IsHeapAllocated())
{
return false;
}

var obj = _heap.Get<HeapObject>(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
: _heap.FormatValue(value);
}
2 changes: 1 addition & 1 deletion Lumi.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<Project Path="Lumi.SemanticAnalyzer/Lumi.SemanticAnalyzer.csproj" />
</Folder>
<Folder Name="/VM/">
<Project Path="Lumi.VM.Heap.Tests/Lumi.VM.Heap.Tests.csproj" />
<Project Path="Lumi.VM.Heap.Tests/Lumi.VM.Heap.Tests.csproj"/>
<Project Path="Lumi.VM.Tests/Lumi.VM.Tests.csproj" Id="8ad99de0-3338-4a31-8f45-f4af1f525e68" />
<Project Path="Lumi.VM/Lumi.VM.csproj" Id="9c4addc6-51bc-4c11-97cb-d4623b4dd17d" />
Comment thread
Mauw94 marked this conversation as resolved.
</Folder>
Expand Down
Loading