diff --git a/FadeBasic/CHANGELOG.md b/FadeBasic/CHANGELOG.md index dfdb502..fe878e9 100644 --- a/FadeBasic/CHANGELOG.md +++ b/FadeBasic/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.0.37] - 2025-03-02 + +### Changed +- VM register max count increased from `byte` to `ulong`. +- VM heap max size increased from ~ 2^31 to ~ 2^64. + ## [0.0.36] - 2025-02-26 ### Fixed diff --git a/FadeBasic/CommandSourceGenerator/Generator.cs b/FadeBasic/CommandSourceGenerator/Generator.cs index 24a1c56..b2e1105 100644 --- a/FadeBasic/CommandSourceGenerator/Generator.cs +++ b/FadeBasic/CommandSourceGenerator/Generator.cs @@ -413,7 +413,8 @@ static string GetReturnSource(CommandDescriptor descriptor) sb.AppendLine($"{nameof(VmConverter)}.{nameof(VmConverter.FromStringSpan)}({result}, out var {resultSpan});"); sb.AppendLine($"{VM}.{nameof(VirtualMachine.heap)}.{nameof(VirtualMachine.heap.AllocateString)}({resultSpan}.Length, out var {resultStrPtr});"); sb.AppendLine($"{VM}.{nameof(VirtualMachine.heap)}.{nameof(VirtualMachine.heap.WriteSpan)}({resultStrPtr}, {resultSpan}.Length, {resultSpan});"); - sb.AppendLine($"var {ptrIntBytes} = {nameof(BitConverter)}.{nameof(BitConverter.GetBytes)}({resultStrPtr});"); + // sb.AppendLine($"var {ptrIntBytes} = {nameof(BitConverter)}.{nameof(BitConverter.GetBytes)}({resultStrPtr});"); + sb.AppendLine($"var {ptrIntBytes} = {nameof(VmPtr)}.{nameof(VmPtr.GetBytes)}(ref {resultStrPtr});"); sb.AppendLine($"{nameof(VmUtil)}.{nameof(VmUtil.PushSpan)}(ref {VM}.{nameof(VirtualMachine.stack)}, {ptrIntBytes}, {TypeCodes.STRING});"); diff --git a/FadeBasic/FadeBasic/FadeBasic.csproj b/FadeBasic/FadeBasic/FadeBasic.csproj index 9ff81aa..9ba2fb6 100644 --- a/FadeBasic/FadeBasic/FadeBasic.csproj +++ b/FadeBasic/FadeBasic/FadeBasic.csproj @@ -11,7 +11,6 @@ FadeBasic.Lang.Core FadeBasic Language Core Fade's actually dotnet embeddable Basic. - diff --git a/FadeBasic/FadeBasic/FadeBasicCommandAttribute.cs b/FadeBasic/FadeBasic/FadeBasicCommandAttribute.cs index 513afb4..fb8ec7f 100644 --- a/FadeBasic/FadeBasic/FadeBasicCommandAttribute.cs +++ b/FadeBasic/FadeBasic/FadeBasicCommandAttribute.cs @@ -23,7 +23,7 @@ public class FromVmAttribute : Attribute public struct RawArg { public T value; - public int address; + public ulong address; public CommandArgRuntimeState state; } } \ No newline at end of file diff --git a/FadeBasic/FadeBasic/Json/IJsonable.cs b/FadeBasic/FadeBasic/Json/IJsonable.cs index 1d99356..96f3cd7 100644 --- a/FadeBasic/FadeBasic/Json/IJsonable.cs +++ b/FadeBasic/FadeBasic/Json/IJsonable.cs @@ -16,6 +16,12 @@ public interface IJsonable void ProcessJson(IJsonOperation op); } + public interface IJsonableSerializationCallbacks : IJsonable + { + void OnAfterDeserialized(); + void OnBeforeSerialize(); + } + public interface IJsonOperation { void Process(IJsonable jsonable); @@ -52,7 +58,7 @@ public static class JsonableExtensions { var instance = new T(); var op = new JsonReadOp(json); - instance.ProcessJson(op); + op.Process(instance); return instance; } @@ -376,7 +382,13 @@ public JsonReadOp(JsonData data) public void Process(IJsonable jsonable) { + jsonable.ProcessJson(this); + + if (jsonable is IJsonableSerializationCallbacks cbr) + { + cbr.OnAfterDeserialized(); + } } public void IncludeField(string name, ref int fieldValue) @@ -390,6 +402,11 @@ public void IncludeField(string name, ref byte fieldValue) fieldValue = (byte)byteValue; } + public void IncludeField(string name, ref ulong fieldValue) + { + throw new NotImplementedException(); + } + public void IncludeField(string name, ref bool fieldValue) { _data.ints.TryGetValue(name, out var byteValue); @@ -538,12 +555,17 @@ void IncludePrim(string name, ref T prim) where T : struct public void Process(IJsonable jsonable) { _sb.Append(JsonConstants.OPEN_BRACKET); + if (jsonable is IJsonableSerializationCallbacks cbr) + { + cbr.OnBeforeSerialize(); + } jsonable.ProcessJson(this); _sb.Append(JsonConstants.CLOSE_BRACKET); } public void IncludeField(string name, ref int fieldValue) => IncludePrim(name, ref fieldValue); public void IncludeField(string name, ref byte fieldValue) => IncludePrim(name, ref fieldValue); + public void IncludeField(string name, ref ulong fieldValue) => IncludePrim(name, ref fieldValue); public void IncludeField(string name, ref bool fieldValue) { diff --git a/FadeBasic/FadeBasic/Launch/DebugSession.cs b/FadeBasic/FadeBasic/Launch/DebugSession.cs index 9b4b06d..1bccf5c 100644 --- a/FadeBasic/FadeBasic/Launch/DebugSession.cs +++ b/FadeBasic/FadeBasic/Launch/DebugSession.cs @@ -683,7 +683,7 @@ void AddVariable(DebugVariable local, bool isGlobal) { typeCode = variable.typeCode, name = local.name, - registerAddress = (byte)variable.regAddr, + registerAddress = variable.regAddr, byteSize = TypeCodes.GetByteSize(variable.typeCode), structType = structName, isGlobal = isGlobal @@ -730,7 +730,7 @@ void AddVariable(DebugVariable local, bool isGlobal) var rankExprs = new IExpressionNode[arrayRankCount]; for (var i = 0; i < arrayRankCount; i++) { - var rankStrideRegAddr = variable.regAddr + arrayRankCount * 2 - (i * 2) ; + var rankStrideRegAddr = variable.regAddr + (ulong)arrayRankCount * 2 - ((ulong)i * 2) ; var rankSizeRegAddr = rankStrideRegAddr - 1; var rankSize = _vm.scopeStack.buffer[variable.scopeIndex] .dataRegisters[rankSizeRegAddr]; @@ -1010,7 +1010,7 @@ void AddVariable(DebugVariable local, bool isGlobal) if (synth.typeCode == TypeCodes.STRUCT) { var ptr = _vm.dataRegisters[synth.registerAddress]; - if (!_vm.heap.TryGetAllocation((int)ptr, out var alloc)) + if (!_vm.heap.TryGetAllocation(VmPtr.FromRaw(ptr), out var alloc)) { return DebugEvalResult.Failed( $"invalid heap, reg=[{synth.registerAddress}] data=[{ptr}] which is not a valid heap pointer"); diff --git a/FadeBasic/FadeBasic/Sdk/Fade.cs b/FadeBasic/FadeBasic/Sdk/Fade.cs index f4138b3..ca66563 100644 --- a/FadeBasic/FadeBasic/Sdk/Fade.cs +++ b/FadeBasic/FadeBasic/Sdk/Fade.cs @@ -437,11 +437,11 @@ public bool TryGetString(string name, out string value, out string error) return false; } - var address = VmUtil.ConvertToInt(scope.dataRegisters[index]); + var address = VmUtil.ConvertToPtr(scope.dataRegisters[index]); return TryReadString(address, out value, out error); } - bool TryReadString(int address, out string value, out string error) + bool TryReadString(VmPtr address, out string value, out string error) { value = null; error = null; @@ -514,7 +514,7 @@ private bool TryGetArray(string name, ref FadeArray value, out string erro } - var address = VmUtil.ConvertToInt(scope.dataRegisters[index]); + var address = VmUtil.ConvertToPtr(scope.dataRegisters[index]); if (!Machine.heap.TryGetAllocation(address, out var allocation)) { error = "invalid memory address"; @@ -592,14 +592,14 @@ public bool TryGetObject(string name, out FadeObject value, out string error) return false; } - var address = VmUtil.ConvertToInt(scope.dataRegisters[index]); + var address = VmUtil.ConvertToPtr(scope.dataRegisters[index]); return TryReadObject(address, out value, out error); } - bool TryReadObject(int address, out FadeObject value, out string error) + bool TryReadObject(VmPtr address, out FadeObject value, out string error) { value = null; error = null; @@ -663,7 +663,8 @@ void ReadMember(ref VmAllocation allocation, int offset, FadeObject value, strin value.doubleFloatFields[fieldName] = BitConverter.ToDouble(memory, member.Offset + offset); break; case TypeCodes.STRING: - var strPtr = BitConverter.ToInt32(memory, member.Offset + offset); + var strPtr = allocation.ptr + member.Offset + offset; + // var strPtr = BitConverter.ToInt32(memory, member.Offset + offset); TryReadString(strPtr, out var strValue, out _); value.stringFields[fieldName] = strValue; break; diff --git a/FadeBasic/FadeBasic/Virtual/Compiler.cs b/FadeBasic/FadeBasic/Virtual/Compiler.cs index ec028a5..218e4d3 100644 --- a/FadeBasic/FadeBasic/Virtual/Compiler.cs +++ b/FadeBasic/FadeBasic/Virtual/Compiler.cs @@ -8,13 +8,15 @@ namespace FadeBasic.Virtual { - public class CompiledVariable : IJsonable + public class CompiledVariable : IJsonable, IJsonableSerializationCallbacks { public byte byteSize; public byte typeCode; public string name; public string structType; - public byte registerAddress; + public ulong registerAddress; + + private string registerAddressSerializer; public bool isGlobal; public void ProcessJson(IJsonOperation op) @@ -23,18 +25,31 @@ public void ProcessJson(IJsonOperation op) op.IncludeField(nameof(typeCode), ref typeCode); op.IncludeField(nameof(name), ref name); op.IncludeField(nameof(structType), ref structType); - op.IncludeField(nameof(registerAddress), ref registerAddress); + op.IncludeField(nameof(registerAddressSerializer), ref registerAddressSerializer); op.IncludeField(nameof(isGlobal), ref isGlobal); } + + public void OnAfterDeserialized() + { + registerAddress = ulong.Parse(registerAddressSerializer); + } + + public void OnBeforeSerialize() + { + registerAddressSerializer = registerAddress.ToString(); + } } - public class CompiledArrayVariable : IJsonable + public class CompiledArrayVariable : IJsonable, IJsonableSerializationCallbacks { public int byteSize; public byte typeCode; public string name; public CompiledType structType; - public byte registerAddress; + public ulong registerAddress; + + private string registerAddressSerializer; + public bool isGlobal; public byte[] rankSizeRegisterAddresses; // an array where the index is the rank, and the value is the ptr to a register whose value holds the size of the rank public byte[] rankIndexScalerRegisterAddresses; // an array where the index is the rank, and the value is the ptr to a register whose value holds the multiplier factor for the rank's indexing @@ -44,11 +59,21 @@ public void ProcessJson(IJsonOperation op) op.IncludeField(nameof(typeCode), ref typeCode); op.IncludeField(nameof(name), ref name); op.IncludeField(nameof(structType), ref structType); - op.IncludeField(nameof(registerAddress), ref registerAddress); + op.IncludeField(nameof(registerAddressSerializer), ref registerAddressSerializer); op.IncludeField(nameof(isGlobal), ref isGlobal); op.IncludeField(nameof(rankSizeRegisterAddresses), ref rankSizeRegisterAddresses); op.IncludeField(nameof(rankIndexScalerRegisterAddresses), ref rankIndexScalerRegisterAddresses); } + + public void OnAfterDeserialized() + { + registerAddress = ulong.Parse(registerAddressSerializer); + } + + public void OnBeforeSerialize() + { + registerAddressSerializer = registerAddress.ToString(); + } } public class CompiledType : IJsonable @@ -95,7 +120,7 @@ public struct FunctionCallReplacement public class CompileScope { - public int registerCount; + public ulong registerCount; private Dictionary _varToReg = new Dictionary(); @@ -111,7 +136,7 @@ public CompileScope(Dictionary varToReg, Dictionary highestAddress) @@ -153,7 +178,7 @@ public CompiledVariable Create(string name, byte typeCode, bool isGlobal, byte r { var compileVar = new CompiledVariable { - registerAddress = (byte)(regOffset + registerCount++), + registerAddress = (regOffset + registerCount++), name = name, typeCode = typeCode, byteSize = TypeCodes.GetByteSize(typeCode), @@ -199,17 +224,53 @@ public class CompilerOptions }; } + public class InternedScopeMetadata : IJsonableSerializationCallbacks + { + public int scopeIndex; + public ulong maxRegisterSize; + private string maxRegisterSizeSerializer; + + public void ProcessJson(IJsonOperation op) + { + op.IncludeField(nameof(scopeIndex), ref scopeIndex); + op.IncludeField(nameof(maxRegisterSizeSerializer), ref maxRegisterSizeSerializer); + } + + public void OnAfterDeserialized() + { + maxRegisterSize = ulong.Parse(maxRegisterSizeSerializer); + } + + public void OnBeforeSerialize() + { + maxRegisterSizeSerializer = maxRegisterSize.ToString(); + } + } - public class InternedData : IJsonable + public class InternedData : IJsonable, IJsonableSerializationCallbacks { public Dictionary types; public Dictionary functions = new Dictionary(); public List strings = new List(); + // public Dictionary scopeMetaDatas = new Dictionary(); + public ulong maxRegisterAddress; + private string maxRegisterAddressSerializer; public void ProcessJson(IJsonOperation op) { op.IncludeField(nameof(types), ref types); op.IncludeField(nameof(functions), ref functions); op.IncludeField(nameof(strings), ref strings); + op.IncludeField(nameof(maxRegisterAddressSerializer), ref maxRegisterAddressSerializer); + } + + public void OnAfterDeserialized() + { + maxRegisterAddress = ulong.Parse(maxRegisterAddressSerializer); + } + + public void OnBeforeSerialize() + { + maxRegisterAddressSerializer = maxRegisterAddress.ToString(); } } @@ -490,6 +551,8 @@ public void PushInternedData() data.types.Add(type.name, type); } + data.maxRegisterAddress = scopeStack.Peek().registerCount; + data.maxRegisterAddress += 1; // an extra 1 for debugging room. var json = data.Jsonify(); var jsonBytes = Encoding.Default.GetBytes(json); _buffer.AddRange(jsonBytes); @@ -765,9 +828,7 @@ private void Compile(FunctionStatement functionStatement) } - - - _buffer.Add(OpCodes.BREAKPOINT); + // compile all the statements... foreach (var statement in functionStatement.statements) { @@ -1373,10 +1434,9 @@ private void Compile(AddressExpression expression) default: // anything else is a registry ptr! var regAddr = variable.registerAddress; - AddPush(_buffer, new byte[] - { - regAddr - }, variable.isGlobal ? TypeCodes.PTR_GLOBAL_REG : TypeCodes.PTR_REG); + _buffer.Add(OpCodes.PUSH); + _buffer.Add(variable.isGlobal ? TypeCodes.PTR_GLOBAL_REG : TypeCodes.PTR_REG); + AddPushULongNoTypeCode(_buffer, regAddr); break; } @@ -1701,7 +1761,6 @@ public void Compile(DeclarationStatement declaration, bool includeDefaultInitial // _buffer.Add(OpCodes.STORE); // _buffer.Add(arrayVar.rankIndexScalerRegisterAddresses[i]); // store the multiplier PushStore(_buffer, arrayVar.rankIndexScalerRegisterAddresses[i], arrayVar.isGlobal); - } @@ -1832,29 +1891,36 @@ public void PushAddress(ArrayIndexReference arrayRefNode) _buffer.Add(OpCodes.MUL); // load the array's ptr onto the stack, this is for the math of the offset - PushLoad(_buffer, compiledArrayVar.registerAddress, compiledArrayVar.isGlobal); - - + PushLoadPtr(_buffer, compiledArrayVar.registerAddress, compiledArrayVar.isGlobal); + // add the offset to the original pointer to get the write location _buffer.Add(OpCodes.ADD); } - static void PushStorePtr(List buffer, byte regAddr, bool isGlobal) + static void PushStorePtr(List buffer, ulong regAddr, bool isGlobal) { buffer.Add(isGlobal ? OpCodes.STORE_PTR_GLOBAL : OpCodes.STORE_PTR); - buffer.Add(regAddr); + // buffer.Add(regAddr); + AddPushULongNoTypeCode(buffer, regAddr); + } + static void PushLoadPtr(List buffer, ulong regAddr, bool isGlobal) + { + buffer.Add(isGlobal ? OpCodes.LOAD_PTR_GLOBAL : OpCodes.LOAD_PTR); + // buffer.Add(regAddr); + AddPushULongNoTypeCode(buffer, regAddr); } - static void PushStore(List buffer, byte registerAddress, bool isGlobal) + static void PushStore(List buffer, ulong registerAddress, bool isGlobal) { buffer.Add(isGlobal ? OpCodes.STORE_GLOBAL : OpCodes.STORE); - buffer.Add(registerAddress); + AddPushULongNoTypeCode(buffer, registerAddress); + } - static void PushLoad(List buffer, byte registerAddress, bool isGlobal) + static void PushLoad(List buffer, ulong registerAddress, bool isGlobal) { buffer.Add(isGlobal ? OpCodes.LOAD_GLOBAL : OpCodes.LOAD); - buffer.Add(registerAddress); + AddPushULongNoTypeCode(buffer, registerAddress); } void CompileStructData(CompiledVariable compiledVar, bool ignoreType=true) @@ -1872,9 +1938,6 @@ void CompileStructData(CompiledVariable compiledVar, bool ignoreType=true) // now, push the pointer where to write the data to- which, we know is the register address PushLoad(_buffer, compiledVar.registerAddress, compiledVar.isGlobal); - - // the address is a struct ref, not an int, so for the write-command to work, we need to cast the struct to an int - CastToInt(); _buffer.Add(OpCodes.WRITE); // consume the ptr, then the length, then the data } @@ -2043,7 +2106,8 @@ void CompileAssignmentLeftHandSide(IVariableNode variable) PushLoad(_buffer, compiledVar.registerAddress, compiledVar.isGlobal); // load the offset of the right side - AddPushInt(_buffer, offset); + // AddPushInt(_buffer, offset); + AddPushPtr(_buffer, new VmPtr(){memoryPtr = offset}, TypeCodes.PTR_HEAP); // sum them, then the result is the ptr on the stack _buffer.Add(OpCodes.ADD); @@ -2082,8 +2146,13 @@ public void Compile(AssignmentStatement assignmentStatement) Compile(assignmentStatement.expression); CompileAssignmentLeftHandSide(assignmentStatement.variable); + if (test++ > 330) + { + + } } + private int test = 0; public void ComputeStructOffsets(CompiledType baseType, IVariableNode right, out int offset, out int writeLength, out byte typeCode) { writeLength = 0; @@ -2182,7 +2251,8 @@ public void Compile(IExpressionNode expr) // push a fake pointer onto the stack... The value gets replaced // at RUNTIME as the machine is allocating the interned strings. // AddPushUInt(_buffer, int.MaxValue, includeTypeCode: false); - AddPushInt(_buffer, int.MaxValue); + // AddPushInt(_buffer, int.MaxValue); + AddPushPtr(_buffer, VmPtr.TEMP, TypeCodes.PTR_HEAP); } else { @@ -2279,12 +2349,15 @@ public void Compile(IExpressionNode expr) AddPushInt(_buffer, readLength); // push the read offset, so that we can add it to the ptr - AddPushInt(_buffer, readOffset); + // AddPushInt(_buffer, readOffset); + AddPushPtr(_buffer, new VmPtr{memoryPtr = readOffset}, TypeCodes.PTR_HEAP); // push the ptr of the variable, and cast it to an int for easy math PushLoad(_buffer, typeCompiledVar.registerAddress, typeCompiledVar.isGlobal); - _buffer.Add(OpCodes.CAST); - _buffer.Add(TypeCodes.INT); + + // TODO: I removed these as part of the PTR refactor? + // _buffer.Add(OpCodes.CAST); + // _buffer.Add(TypeCodes.INT); // add those two op codes back together... _buffer.Add(OpCodes.ADD); @@ -2348,9 +2421,7 @@ public void Compile(IExpressionNode expr) // load the size up AddPushInt(_buffer, structType.byteSize); - // PushAddress(arrayRef); PushLoad(_buffer, compiledVar.registerAddress, compiledVar.isGlobal); - CastToInt(); // read, it'll find the ptr, size, and then place the data onto the stack _buffer.Add(OpCodes.READ); @@ -2584,6 +2655,18 @@ private static void AddPushTypeFormat(List buffer, ref HeapTypeFormat form buffer.Add(OpCodes.PUSH_TYPE_FORMAT); HeapTypeFormat.AddToBuffer(ref format, buffer); } + + private static void AddPushPtr(List buffer, VmPtr ptr, byte typeCode) + { + buffer.Add(OpCodes.PUSH); + buffer.Add(typeCode); + var value = VmPtr.GetBytes(ref ptr); + for (var i = 0; i < value.Length; i++) + // for (var i = value.Length - 1; i >= 0; i--) + { + buffer.Add(value[i]); + } + } private static void AddPushInt(List buffer, int x) { @@ -2596,9 +2679,18 @@ private static void AddPushInt(List buffer, int x) buffer.Add(value[i]); } } + + private static void AddPushULongNoTypeCode(List buffer, ulong x) + { + var value = BitConverter.GetBytes(x); + for (var i = 0 ; i < value.Length; i ++) + { + buffer.Add(value[i]); + } + } + private static void AddPushUInt(List buffer, uint x, bool includeTypeCode=true) { - if (includeTypeCode) { buffer.Add(OpCodes.PUSH); diff --git a/FadeBasic/FadeBasic/Virtual/DebugUtil.cs b/FadeBasic/FadeBasic/Virtual/DebugUtil.cs index fb4915d..8bc074f 100644 --- a/FadeBasic/FadeBasic/Virtual/DebugUtil.cs +++ b/FadeBasic/FadeBasic/Virtual/DebugUtil.cs @@ -10,12 +10,12 @@ public class DebugRuntimeVariable public readonly byte typeCode; public ulong rawValue; // could be a ptr. public readonly int scopeIndex; - public readonly int regAddr; + public readonly ulong regAddr; public readonly VmAllocation allocation; public readonly VirtualMachine vm; - public DebugRuntimeVariable(VirtualMachine vm, string name, byte typeCode, ulong rawValue, ref VmAllocation allocation, int scopeIndex, int regAddr) + public DebugRuntimeVariable(VirtualMachine vm, string name, byte typeCode, ulong rawValue, ref VmAllocation allocation, int scopeIndex, ulong regAddr) { this.vm = vm; this.name = name; @@ -26,7 +26,7 @@ public DebugRuntimeVariable(VirtualMachine vm, string name, byte typeCode, ulong this.regAddr = regAddr; } - public DebugRuntimeVariable(VirtualMachine vm, string name, byte typeCode, ulong rawValue, int scopeIndex, int regAddr) + public DebugRuntimeVariable(VirtualMachine vm, string name, byte typeCode, ulong rawValue, int scopeIndex, ulong regAddr) { this.vm = vm; @@ -39,7 +39,7 @@ public DebugRuntimeVariable(VirtualMachine vm, string name, byte typeCode, ulong // we know at this moment if the rawValue is a ptr or not... if (typeCode == TypeCodes.STRUCT || typeCode == TypeCodes.STRING) { - if (!this.vm.heap.TryGetAllocation((int) rawValue, out allocation)) + if (!this.vm.heap.TryGetAllocation(VmPtr.FromRaw(rawValue), out allocation)) { throw new InvalidOperationException("There is no allocation for the struct reference. hh"); } @@ -66,7 +66,7 @@ public string GetValueDisplay() case TypeCodes.BYTE: return VmUtil.ConvertToByte(rawValue).ToString(); case TypeCodes.STRING: - var address = (int)rawValue; + var address = VmPtr.FromRaw(rawValue); if (vm.heap.TryGetAllocationSize(address, out var strSize)) { vm.heap.Read(address, strSize, out var strBytes); @@ -142,7 +142,7 @@ public int GetElementCount(out byte rankCount, out bool isArray) * Then move backwards by our current rank index (-arrayRankIndex*2) * And then of the two registers, the element size is 1 back. */ - var elementRegAddr = regAddr + rankCount * 2 - (arrayRankIndex * 2 ) - 1; + ulong elementRegAddr = regAddr + (ulong)rankCount * 2 - ((ulong)arrayRankIndex * 2 ) - 1; var elementCount = scope.dataRegisters[elementRegAddr]; return (int)elementCount; @@ -344,7 +344,7 @@ public DebugScope Expand(int variableRequestVariableId) var elementTypeCode = variable.allocation.format.typeCode; // TODO: replace this with the stide in the dataRegisters... - var elementRegAddr = variable.regAddr + arrayRanks * 2 - (variable.arrayRankIndex * 2 ); + var elementRegAddr = variable.regAddr + (ulong)arrayRanks * 2 - ((ulong)variable.arrayRankIndex * 2 ); var strideLength = (int)_vm.scopeStack.buffer[variable.scopeIndex].dataRegisters[elementRegAddr]; _logger.Log($"found stride length=[{strideLength}] at reg=[{elementRegAddr}]"); var elementSize = (int)TypeCodes.GetByteSize(elementTypeCode); @@ -384,7 +384,7 @@ public DebugScope Expand(int variableRequestVariableId) var ptr = variable.rawValue + (ulong)(elementSize * i * strideLength); var alloc = new VmAllocation { - ptr = (int)ptr, + ptr = VmPtr.FromRaw(ptr), length = elementSize, format = new HeapTypeFormat { @@ -444,7 +444,7 @@ public DebugScope Expand(int variableRequestVariableId) } else { - _vm.heap.ReadSpan((int)variable.rawValue + elementSize * i, elementSize, + _vm.heap.ReadSpan(VmPtr.FromRaw(variable.rawValue) + elementSize * i, elementSize, out var fieldSpan); subVariable.value = VmUtil.ConvertValueToDisplayString(elementTypeCode, _vm, ref fieldSpan); @@ -479,7 +479,7 @@ public DebugScope Expand(int variableRequestVariableId) var ptr = variable.rawValue + (ulong)field.offset; var alloc = new VmAllocation { - ptr = (int)ptr, + ptr = VmPtr.FromRaw(ptr), length = field.length, format = new HeapTypeFormat { @@ -523,7 +523,7 @@ public DebugScope Expand(int variableRequestVariableId) }; evalNameToId[subVariable.evalName] = subVariable.id; - _vm.heap.ReadSpan((int)variable.rawValue + field.offset, field.length, out var fieldSpan); + _vm.heap.ReadSpan(VmPtr.FromRaw(variable.rawValue) + field.offset, field.length, out var fieldSpan); VmUtil.TryGetVariableTypeDisplay(field.typeCode, out subVariable.type); subVariable.value = VmUtil.ConvertValueToDisplayString(field.typeCode, _vm, ref fieldSpan); @@ -673,7 +673,7 @@ public static class DebugUtil public static Dictionary LookupVariablesFromScope(VirtualMachine vm, Dictionary results, DebugData dbg, int vmScopeIndex, bool global) { var scope = vm.scopeStack.buffer[vmScopeIndex]; - for (var scopeIndex = 0; scopeIndex < scope.dataRegisters.Length; scopeIndex++) + for (ulong scopeIndex = 0; scopeIndex < (ulong)scope.dataRegisters.LongLength; scopeIndex++) { var insIndex = scope.insIndexes[scopeIndex]; if (!dbg.insToVariable.TryGetValue(insIndex, out var variable)) @@ -689,7 +689,7 @@ public static Dictionary LookupVariablesFromScope( if (variable.isPtr > 0) { - if (!vm.heap.TryGetAllocation((int)scope.dataRegisters[scopeIndex], out var allocation)) + if (!vm.heap.TryGetAllocation(VmPtr.FromRaw(scope.dataRegisters[scopeIndex]), out var allocation)) { throw new InvalidOperationException($"Could not get allocation for pointer based variable=[{variable.name}]"); } diff --git a/FadeBasic/FadeBasic/Virtual/OpCodes.cs b/FadeBasic/FadeBasic/Virtual/OpCodes.cs index db16256..0263459 100644 --- a/FadeBasic/FadeBasic/Virtual/OpCodes.cs +++ b/FadeBasic/FadeBasic/Virtual/OpCodes.cs @@ -29,11 +29,11 @@ public static class TypeCodes public const byte DINT = 0x06; // 8 bytes (long) public const byte DFLOAT = 0x07; // 8 bytes (double) public const byte VOID = 0x08; // 0 bytes - public const byte STRING = 0x09; // 4 bytes (ptr) - public const byte PTR_REG = 0x0A; // 1 byte (registry ptr) - public const byte PTR_HEAP = 0x0B; // 4 bytes (heap ptr) - public const byte STRUCT = 0x0C; // 4 bytes (ptr) - public const byte PTR_GLOBAL_REG = 0x0D; // 1 byte (global registry ptr) + public const byte STRING = 0x09; // 8 bytes (ptr) + public const byte PTR_REG = 0x0A; // 8 byte (registry ptr) + public const byte PTR_HEAP = 0x0B; // 8 bytes (heap ptr) + public const byte STRUCT = 0x0C; // 8 bytes (ptr), bucket and ptr + public const byte PTR_GLOBAL_REG = 0x0D; // 8 byte (global registry ptr) public const byte ANY = 254; // this isn't a real type code, it is a fake number used for calling C# methods public const byte VM = 255; // this isn't a real type code, it is used as a hack in calling C# methods @@ -50,9 +50,9 @@ public static class TypeCodes 60, // dfloat 30, // void 30, // string (int ptr) - 10, // ptr_reg - 10, // ptr_heap - 10, // struct (int ptr) + 100, // ptr_reg + 100, // ptr_heap + 100, // struct (int ptr) }; public static readonly byte[] SIZE_TABLE = new byte[] @@ -66,11 +66,11 @@ public static class TypeCodes 8, // dint 8, // dfloat 0, // void - 4, // string (int ptr) - 1, // ptr_reg - 4, // ptr_heap - 4, // struct (int ptr) - 1, // global ptr_reg + 8, // string (int ptr) + 8, // ptr_reg + 8, // ptr_heap + 8, // struct (int ptr) + 8, // global ptr_reg }; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -350,5 +350,11 @@ public static class OpCodes /// The intersection of and /// public const byte STORE_PTR_GLOBAL = 48; + + /// + /// Similar to , but the value is actually a pointer + /// + public const byte LOAD_PTR_GLOBAL = 57; + public const byte LOAD_PTR = 58; } } \ No newline at end of file diff --git a/FadeBasic/FadeBasic/Virtual/VirtualMachine.cs b/FadeBasic/FadeBasic/Virtual/VirtualMachine.cs index f53f884..85c39f8 100644 --- a/FadeBasic/FadeBasic/Virtual/VirtualMachine.cs +++ b/FadeBasic/FadeBasic/Virtual/VirtualMachine.cs @@ -40,7 +40,7 @@ public static bool IsPtr(byte flags) public int[] insIndexes; public byte[] flags; - public VirtualScope(int initialCapacity) + public VirtualScope(ulong initialCapacity) { dataRegisters = new ulong[initialCapacity]; typeRegisters = new byte[initialCapacity]; @@ -105,18 +105,19 @@ public VirtualMachine(byte[] program) { this.program = program; shouldThrowRuntimeException = true; - globalScope = scope = new VirtualScope(256); // scope = new VirtualScope(256); scopeStack = new FastStack(16); methodStack = new FastStack(16); heap = new VmHeap(128); - scopeStack.Push(globalScope); instructionIndex = 4; internedDataInstructionIndex = BitConverter.ToInt32(program, 0); ReadInternedData(); + globalScope = scope = new VirtualScope(internedData.maxRegisterAddress); + scopeStack.Push(globalScope); + // scopeStack.Push(scope); } @@ -149,8 +150,8 @@ public void Suspend() /// public struct VmState { - public byte vTypeCode, typeCode, addr, size; - public ulong data; + public byte vTypeCode, typeCode, size; + public ulong data, addr; public int insPtr; } @@ -185,7 +186,7 @@ void ReadInternedData() heap.AllocateString(size, out var ptr); // this interned string should never be free'd, because we don't know when it will need to be accessed. - heap.IncrementRefCount((ulong)ptr); + heap.IncrementRefCount(ptr); var span = new byte[size]; for (var i = 0; i < str.value.Length; i++) { @@ -199,7 +200,7 @@ void ReadInternedData() heap.Write(ptr, size, span); - var ptrBytes = BitConverter.GetBytes(ptr); + var ptrBytes = VmPtr.GetBytes(ref ptr); foreach (var index in str.indexReferences) { // replace the ptr starting at index with the actual assigned ptr @@ -208,6 +209,11 @@ void ReadInternedData() program[index + 3] = ptrBytes[1]; program[index + 4] = ptrBytes[2]; program[index + 5] = ptrBytes[3]; + + program[index + 6] = ptrBytes[4]; + program[index + 7] = ptrBytes[5]; + program[index + 8] = ptrBytes[6]; + program[index + 9] = ptrBytes[7]; } } } @@ -227,8 +233,8 @@ public void Execute2(int instructionBatchCount=1000) // these pointer/data values need to be held between instruction evaluations, and therefor stay in the state. byte vTypeCode = state.vTypeCode, typeCode = state.typeCode; - ulong data = state.data; - byte addr = state.addr, size = state.size; + ulong data = state.data, addr = state.addr; + byte size = state.size; int insPtr = state.insPtr; // var sw = new Stopwatch(); @@ -257,7 +263,7 @@ public void Execute2(int instructionBatchCount=1000) break; case OpCodes.PUSH_SCOPE: - var newScope = new VirtualScope(64); + var newScope = new VirtualScope(internedData.maxRegisterAddress); scopeStack.Push(newScope); scope = newScope; break; @@ -276,7 +282,6 @@ public void Execute2(int instructionBatchCount=1000) var ptr = vScope.dataRegisters[scopeIndex]; if (isPtr && vScope.insIndexes[scopeIndex] > 0) { - heap.TryDecrementRefCount(ptr); } } @@ -526,9 +531,9 @@ public void Execute2(int instructionBatchCount=1000) VmUtil.PushSpan(ref stack, cSpan, TypeCodes.INT); break; case OpCodes.STORE: - // read a register location, which is always 1 byte. - addr = Advance(); + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); + VmUtil.ReadSpanAsUInt(ref stack, out data); scope.dataRegisters[addr] = data; scope.typeRegisters[addr] = typeCode; @@ -539,7 +544,8 @@ public void Execute2(int instructionBatchCount=1000) case OpCodes.STORE_PTR: // read a register location, which is always 1 byte. - addr = Advance(); + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); + VmUtil.ReadSpanAsUInt(ref stack, out data); if (scope.insIndexes[addr] > 0) @@ -563,7 +569,8 @@ public void Execute2(int instructionBatchCount=1000) case OpCodes.STORE_PTR_GLOBAL: // read a register location, which is always 1 byte. - addr = Advance(); + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); + VmUtil.ReadSpanAsUInt(ref stack, out data); if (globalScope.insIndexes[addr] > 0) @@ -581,7 +588,7 @@ public void Execute2(int instructionBatchCount=1000) break; case OpCodes.STORE_GLOBAL: - addr = Advance(); + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); VmUtil.ReadSpanAsUInt(ref stack, out data); globalScope.dataRegisters[addr] = data; globalScope.typeRegisters[addr] = typeCode; @@ -590,7 +597,7 @@ public void Execute2(int instructionBatchCount=1000) break; case OpCodes.LOAD: - addr = Advance(); + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); typeCode = scope.typeRegisters[addr]; data = scope.dataRegisters[addr]; @@ -598,9 +605,29 @@ public void Execute2(int instructionBatchCount=1000) aBytes = BitConverter.GetBytes(data); stack.PushSpanAndType(new ReadOnlySpan(aBytes), typeCode, size); + break; + case OpCodes.LOAD_PTR: + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); + + typeCode = TypeCodes.PTR_HEAP;// globalScope.typeRegisters[addr]; + data = scope.dataRegisters[addr]; + size = TypeCodes.GetByteSize(typeCode); + aBytes = BitConverter.GetBytes(data); + stack.PushSpanAndType(new ReadOnlySpan(aBytes), typeCode, size); + + break; + case OpCodes.LOAD_PTR_GLOBAL: + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); + + typeCode = TypeCodes.PTR_HEAP;// globalScope.typeRegisters[addr]; + data = globalScope.dataRegisters[addr]; + size = TypeCodes.GetByteSize(typeCode); + aBytes = BitConverter.GetBytes(data); + stack.PushSpanAndType(new ReadOnlySpan(aBytes), typeCode, size); + break; case OpCodes.LOAD_GLOBAL: - addr = Advance(); + VmUtil.ReadRegAddress(program, ref instructionIndex, out addr); typeCode = globalScope.typeRegisters[addr]; data = globalScope.dataRegisters[addr]; @@ -619,8 +646,8 @@ public void Execute2(int instructionBatchCount=1000) VmUtil.ReadAsInt(ref stack, out var allocLength); heap.Allocate(ref format, allocLength, out var allocPtr); // push the address onto the stack - bBytes = BitConverter.GetBytes(allocPtr); - VmUtil.PushSpan(ref stack, bBytes, TypeCodes.INT); + bBytes = VmPtr.GetBytes(ref allocPtr); + VmUtil.PushSpan(ref stack, bBytes, TypeCodes.PTR_HEAP); break; case OpCodes.DISCARD: @@ -657,7 +684,7 @@ public void Execute2(int instructionBatchCount=1000) break; case OpCodes.READ: - VmUtil.ReadAsInt(ref stack, out var readPtr); + VmUtil.ReadAsVmPtr(ref stack, out var readPtr); VmUtil.ReadAsInt(ref stack, out var readLength); heap.Read(readPtr, readLength, out aBytes); stack.PushSpan(aBytes, readLength); @@ -701,6 +728,8 @@ public void Execute2(int instructionBatchCount=1000) } + + public int test = 0; void TriggerRuntimeError(VirtualRuntimeError error) { diff --git a/FadeBasic/FadeBasic/Virtual/VmHeap.cs b/FadeBasic/FadeBasic/Virtual/VmHeap.cs index f099dfa..29dcf9e 100644 --- a/FadeBasic/FadeBasic/Virtual/VmHeap.cs +++ b/FadeBasic/FadeBasic/Virtual/VmHeap.cs @@ -1,82 +1,192 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; namespace FadeBasic.Virtual { public struct VmAllocation { - public int ptr; + public VmPtr ptr; public int length; public HeapTypeFormat format; } + + /// + /// The max size of an array in C# is ~2 billion, or roughly 2^31. The 1 unused bit on the int isn't + /// useful enough to do much with. + /// + /// A ptr needs to be able to address more than 2^31 in a 64bit OS world. + /// A second integer is kept to add more bits, and acts as a way to bucketize the original 31 bit pointer. + /// + /// An object is not allowed to exceed the bucket size, which is 2^31 (C#'s addressable array size) + /// + public struct VmPtr : IEquatable + { + + + public static VmPtr operator +(VmPtr a, int b) + { + return new VmPtr + { + bucketPtr = a.bucketPtr, + memoryPtr = a.memoryPtr + b + }; + } + + public readonly static VmPtr TEMP = new VmPtr + { + bucketPtr = int.MaxValue, memoryPtr = int.MaxValue + }; + + public static VmPtr FromRaw(ulong raw) + { + var bytes = BitConverter.GetBytes(raw); + return FromBytes(bytes); + } + + public static VmPtr FromBytes(ReadOnlySpan span) => FromBytes(span.ToArray()); + + public static VmPtr FromBytes(byte[] bytes) + { + // TODO: after all tests pass, flip the bytes around so that ptrs are more easily readable. + var ptr = new VmPtr + { + bucketPtr = BitConverter.ToInt32(bytes, 0), + memoryPtr = BitConverter.ToInt32(bytes, 4), + }; + + return ptr; + } + + public static byte[] GetBytes(ref VmPtr ptr) + { + var bytes = new byte[sizeof(int) * 2]; + var bucketBytes = BitConverter.GetBytes(ptr.bucketPtr); + var memoryBytes = BitConverter.GetBytes(ptr.memoryPtr); + + for (var i = 0; i < bucketBytes.Length; i++) + { + bytes[i] = bucketBytes[i]; + bytes[i + 4] = memoryBytes[i]; + } + + return bytes; + } + + + public static ulong GetRaw(ref VmPtr ptr) + { + return BitConverter.ToUInt64(GetBytes(ref ptr), 0); + } + + + // https://learn.microsoft.com/en-us/dotnet/api/system.array?view=net-9.0&redirectedfrom=MSDN#remarks + public const uint MAX_ARRAY_SIZE = 0X7FEFFFFF; // ~2 billion + + public int bucketPtr; + public int memoryPtr; + + public bool Equals(VmPtr other) + { + return bucketPtr == other.bucketPtr && memoryPtr == other.memoryPtr; + } + + public override bool Equals(object obj) + { + return obj is VmPtr other && Equals(other); + } + + public override int GetHashCode() + { + unchecked + { + return (bucketPtr * 397) ^ memoryPtr; + } + } + + public override string ToString() + { + return $"ptr(m={memoryPtr}, b={bucketPtr})"; + } + } + + public class HeapData : List + { + public int BucketSize(VmPtr ptr) => this[ptr.bucketPtr].Length; + } public struct VmHeap { - public byte[] memory; - private int _cursor; + // private byte[] memory; + private HeapData memory; + private VmPtr _cursor; - public int Cursor => _cursor; + public VmPtr Cursor => _cursor; /// /// key: a ptr /// value: a length (and metadata) /// /// If it exists in this dictionary, then it is "leased" /// - private Dictionary _allocations;// = new Dictionary(); + private Dictionary _allocations;// = new Dictionary(); - private Dictionary> _lengthToPtrs;// = new Dictionary>(); + private Dictionary> _lengthToPtrs;// = new Dictionary>(); - private Dictionary _ptrToRefCount; + private Dictionary _ptrToRefCount; public int Allocations => _allocations.Count; public VmHeap(int initialCapacity) { - memory = new byte[initialCapacity]; - _cursor = 0; - _allocations = new Dictionary(); - _lengthToPtrs = new Dictionary>(); - _ptrToRefCount = new Dictionary(); + memory = new HeapData + { + new byte[initialCapacity] + }; + _cursor = new VmPtr(); + _allocations = new Dictionary(); + _lengthToPtrs = new Dictionary>(); + _ptrToRefCount = new Dictionary(); } - public void Write(int ptr, int length, byte[] data) + public void Write(VmPtr ptr, int length, byte[] data) { for (var i = 0; i < length; i++) { - memory[ptr + i] = data[i]; + memory[ptr.bucketPtr][ptr.memoryPtr + i] = data[i]; } } - public void WriteSpan(int ptr, int length, ReadOnlySpan data) + public void WriteSpan(VmPtr ptr, int length, ReadOnlySpan data) { - for (var i = 0; i < length; i++) + for (int i = 0; i < length; i++) { - memory[ptr + i] = data[i]; + memory[ptr.bucketPtr][ptr.memoryPtr + i] = data[i]; } } - public void ReadSpan(int ptr, int length, out ReadOnlySpan memory) + public void ReadSpan(VmPtr ptr, int length, out ReadOnlySpan memory) { - memory = new ReadOnlySpan(this.memory, ptr, length); + + memory = new ReadOnlySpan(this.memory[ptr.bucketPtr], ptr.memoryPtr, length); } - public void Read(int ptr, int length, out byte[] memory) + public void Read(VmPtr ptr, int length, out byte[] memory) { memory = new byte[length]; - for (var i = ptr; i < ptr + length; i++) + for (var i = ptr.memoryPtr; i < ptr.memoryPtr + length; i++) { // var memAddr = length - (i - ptr) - 1; - var memAddr = i - ptr; - memory[memAddr] = this.memory[i]; + var memAddr = i - ptr.memoryPtr; + memory[memAddr] = this.memory[ptr.bucketPtr][i]; } } - public void Copy(int srcPtr, int dstPtr, int length) + public void Copy(VmPtr srcPtr, VmPtr dstPtr, int length) { for (var i = 0; i < length; i++) { - memory[dstPtr + i] = memory[srcPtr + i]; + memory[dstPtr.bucketPtr][dstPtr.memoryPtr + i] = memory[srcPtr.bucketPtr][srcPtr.memoryPtr + i]; } } @@ -85,12 +195,12 @@ public void Copy(int srcPtr, int dstPtr, int length) // var size = arrayLength * TypeCodes.GetByteSize(typeCode); // Allocate(ref HeapTypeFormat.STRING_FORMAT, size, out ptr); // } - public void AllocateString(int length, out int ptr) + public void AllocateString(int length, out VmPtr ptr) { Allocate(ref HeapTypeFormat.STRING_FORMAT, length, out ptr); } - public void Free(int ptr) + public void Free(VmPtr ptr) { if (!_allocations.TryGetValue(ptr, out var allocation)) { @@ -101,14 +211,15 @@ public void Free(int ptr) // _ptrToFreed[ptr] = length; if (!_lengthToPtrs.TryGetValue(allocation.length, out var ptrs)) { - ptrs = _lengthToPtrs[allocation.length] = new Stack(); + ptrs = _lengthToPtrs[allocation.length] = new Stack(); } ptrs.Push(ptr); } - public void Allocate(ref HeapTypeFormat format, int size, out int ptr) + public void Allocate(ref HeapTypeFormat format, int size, out VmPtr ptr) { + // TODO: assert that the size is less than the max array size. // If there is something with the exact size we need, grab it! if (_lengthToPtrs.TryGetValue(size, out var availablePtrs) && availablePtrs.Count > 0) @@ -123,14 +234,34 @@ public void Allocate(ref HeapTypeFormat format, int size, out int ptr) return; } - if (_cursor + size >= memory.Length) + if (_cursor.memoryPtr + size >= memory.BucketSize(_cursor)) { - - - - while (_cursor + size >= memory.Length) + if (_cursor.memoryPtr + size >= memory.BucketSize(_cursor) && (uint)_cursor.memoryPtr + (uint)size >= VmPtr.MAX_ARRAY_SIZE) { - Array.Resize(ref memory, memory.Length * 2); + // game over! We need a new bucket! + _cursor.bucketPtr += 1; + _cursor.memoryPtr = 0; + + // allocate the next bucket at the full size, since we've exhausted one anyway... + // this does mean that the program mem jumps from 2GB to 4GB. You cannot have + // 3GB working set :( + memory.Add(new byte[VmPtr.MAX_ARRAY_SIZE]); + + } + else + { + // expand the current bucket size + while (_cursor.memoryPtr + size >= memory.BucketSize(_cursor)) + { + var nextSize = (uint)memory.BucketSize(_cursor) * 2; + if (nextSize >= VmPtr.MAX_ARRAY_SIZE) + { + nextSize = VmPtr.MAX_ARRAY_SIZE; + } + var arr = memory[_cursor.bucketPtr]; + Array.Resize(ref arr, (int)nextSize); + memory[_cursor.bucketPtr] = arr; + } } } @@ -144,15 +275,15 @@ public void Allocate(ref HeapTypeFormat format, int size, out int ptr) }; if (size == 0) { - _cursor += 1; // increase the cursor by 1, so that this spot is held as "empty" + _cursor.memoryPtr += 1; // increase the cursor by 1, so that this spot is held as "empty" } else { - _cursor += size; + _cursor.memoryPtr += size; } } - public void GetAllocationSize(int ptr, out int size) + public void GetAllocationSize(VmPtr ptr, out int size) { if (!_allocations.TryGetValue(ptr, out var allocation)) { @@ -161,28 +292,28 @@ public void GetAllocationSize(int ptr, out int size) size = allocation.length; } - public bool TryGetAllocationSize(int ptr, out int size) + public bool TryGetAllocationSize(VmPtr ptr, out int size) { var success = _allocations.TryGetValue(ptr, out var allocation); size = allocation.length; return success; } - public bool TryGetAllocation(int ptr, out VmAllocation allocation) + public bool TryGetAllocation(VmPtr ptr, out VmAllocation allocation) { return _allocations.TryGetValue(ptr, out allocation); } - public void IncrementRefCount(ulong ptr) + public void IncrementRefCount(ulong ptr) => IncrementRefCount(VmPtr.FromRaw(ptr)); + public void IncrementRefCount(VmPtr ptr) { - var key = (int)ptr; - if (_ptrToRefCount.TryGetValue(key, out var refCount)) + if (_ptrToRefCount.TryGetValue(ptr, out var refCount)) { - _ptrToRefCount[key] = refCount + 1; + _ptrToRefCount[ptr] = refCount + 1; } else { - _ptrToRefCount[key] = 1; + _ptrToRefCount[ptr] = 1; } } @@ -206,14 +337,14 @@ public void Sweep() // } // } } - - public void TryDecrementRefCount(ulong ptr) - { - var key = (int)ptr; - if (_ptrToRefCount.TryGetValue(key, out var refCount)) + public void TryDecrementRefCount(ulong ptr) => TryDecrementRefCount(VmPtr.FromRaw(ptr)); + + public void TryDecrementRefCount(VmPtr ptr) + { + if (_ptrToRefCount.TryGetValue(ptr, out var refCount)) { - _ptrToRefCount[key] = refCount - 1; + _ptrToRefCount[ptr] = refCount - 1; if (refCount == 1) { diff --git a/FadeBasic/FadeBasic/Virtual/VmUtil.cs b/FadeBasic/FadeBasic/Virtual/VmUtil.cs index 60d92d0..7638949 100644 --- a/FadeBasic/FadeBasic/Virtual/VmUtil.cs +++ b/FadeBasic/FadeBasic/Virtual/VmUtil.cs @@ -127,12 +127,18 @@ public static byte GetTypeCode(VariableType vt) } } + public static VmPtr ConvertToPtr(ulong rawValue) + { + return VmPtr.FromRaw(rawValue); + } + public static int ConvertToInt(ulong rawValue) { var outputRegisterBytes = BitConverter.GetBytes(rawValue); int output = BitConverter.ToInt32(outputRegisterBytes, 0); return output; } + public static float ConvertToFloat(ulong rawValue) { var outputRegisterBytes = BitConverter.GetBytes(rawValue); @@ -172,27 +178,6 @@ public static double ConvertToDFloat(ulong rawValue) return BitConverter.ToDouble(outputRegisterBytes, 0); } - public static string GetTypeName(byte typeCode, VirtualMachine vm, ulong rawValue) - { - if (!VmUtil.TryGetVariableTypeDisplay(typeCode, out var typeName)) - { - return "UNKNOWN"; - } - - if (typeCode == TypeCodes.STRUCT) - { - // the rawValue is a ptr into the heap... - if (vm.heap.TryGetAllocation((int) rawValue, out var allocation)) - { - var typeId = allocation.format.typeId; - var type = vm.typeTable[typeId]; - typeName = type.name; - } - } - - return typeName; - } - public static string ConvertValueToDisplayString(byte typeCode, VirtualMachine vm, ref ReadOnlySpan span) { switch (typeCode) @@ -216,7 +201,7 @@ public static string ConvertValueToDisplayString(byte typeCode, VirtualMachine v var num2 = BitConverter.ToDouble(span.ToArray(), 0); return num2.ToString(CultureInfo.InvariantCulture); case TypeCodes.STRING: - var address = BitConverter.ToInt32(span.ToArray(), 0); + var address = VmPtr.FromBytes(span.ToArray()); if (vm.heap.TryGetAllocationSize(address, out var strSize)) { vm.heap.Read(address, strSize, out var strBytes); @@ -264,7 +249,7 @@ public static void GetBytes(T value, out byte[] bytes) throw new NotImplementedException("cannot convert unknown type to bytes " + value + " typeof " + typeof(T)); } } - public static void HandleValue(VirtualMachine vm, T value, byte typeCode, CommandArgRuntimeState state, int address) where T : struct + public static void HandleValue(VirtualMachine vm, T value, byte typeCode, CommandArgRuntimeState state, ulong address) where T : struct { switch (state) { @@ -279,7 +264,8 @@ public static void HandleValue(VirtualMachine vm, T value, byte typeCode, Com case CommandArgRuntimeState.HeapRef: var size = TypeCodes.GetByteSize(typeCode); GetBytes(value, out var heapBytes); - vm.heap.Write(address, size, heapBytes); + var ptrAddr = VmPtr.FromRaw(address); + vm.heap.Write(ptrAddr, size, heapBytes); break; case CommandArgRuntimeState.Value: // do nothing. @@ -288,17 +274,17 @@ public static void HandleValue(VirtualMachine vm, T value, byte typeCode, Com } - public static void HandleValueString(VirtualMachine vm, string value, byte typeCode, CommandArgRuntimeState state, int address) + public static void HandleValueString(VirtualMachine vm, string value, byte typeCode, CommandArgRuntimeState state, ulong address) { byte[] strBytes; - int strPtr; + VmPtr strPtr; switch (state) { case CommandArgRuntimeState.RegisterRef: VmConverter.FromString(value, out strBytes); vm.heap.Allocate(ref HeapTypeFormat.STRING_FORMAT, strBytes.Length, out strPtr); vm.heap.Write(strPtr, strBytes.Length, strBytes); - vm.dataRegisters[address] = BitConverter.ToUInt32(BitConverter.GetBytes(strPtr), 0); + vm.dataRegisters[address] = VmPtr.GetRaw(ref strPtr); vm.typeRegisters[address] = typeCode; break; case CommandArgRuntimeState.HeapRef: @@ -306,7 +292,7 @@ public static void HandleValueString(VirtualMachine vm, string value, byte typeC VmConverter.FromString(value, out strBytes); vm.heap.Allocate(ref HeapTypeFormat.STRING_FORMAT,strBytes.Length, out strPtr); vm.heap.Write(strPtr, strBytes.Length, strBytes); - vm.heap.Write(address, 4, BitConverter.GetBytes(strPtr)); + vm.heap.Write(VmPtr.FromRaw(address), 8, VmPtr.GetBytes(ref strPtr)); break; case CommandArgRuntimeState.Value: // do nothing. @@ -317,10 +303,11 @@ public static void HandleValueString(VirtualMachine vm, string value, byte typeC // public static void ReadValue(VirtualMachine vm, string defaultValue, out string ) public static void ReadValueString(VirtualMachine vm, string defaultValue, out string value, - out CommandArgRuntimeState state, out int address) + out CommandArgRuntimeState state, out ulong address) { ReadSpan(ref vm.stack, out var typeCode, out var span); - int strPtr = 0, strSize = 0; + int strSize = 0; + VmPtr strPtr = default; switch (typeCode) { case TypeCodes.VOID: @@ -330,12 +317,11 @@ public static void ReadValueString(VirtualMachine vm, string defaultValue, out s break; case TypeCodes.PTR_REG: state = CommandArgRuntimeState.RegisterRef; - address = (int) span[0]; + address = MemoryMarshal.Read(span); var data = vm.dataRegisters[address]; typeCode = vm.typeRegisters[address]; // TODO: we could validate that typeCode is equal to the given typeCode for - var bytes = BitConverter.GetBytes(data); - strPtr = (int)BitConverter.ToUInt32(bytes, 0); + strPtr = VmPtr.FromRaw(data); if (vm.heap.TryGetAllocationSize(strPtr, out strSize)) { vm.heap.Read(strPtr, strSize, out var strBytes); @@ -348,12 +334,11 @@ public static void ReadValueString(VirtualMachine vm, string defaultValue, out s break; case TypeCodes.PTR_GLOBAL_REG: state = CommandArgRuntimeState.GlobalRegisterRef; - address = (int) span[0]; + address = MemoryMarshal.Read(span); data = vm.globalScope.dataRegisters[address]; typeCode = vm.globalScope.typeRegisters[address]; // TODO: we could validate that typeCode is equal to the given typeCode for - bytes = BitConverter.GetBytes(data); - strPtr = (int)BitConverter.ToUInt32(bytes, 0); + strPtr = VmPtr.FromRaw(data); if (vm.heap.TryGetAllocationSize(strPtr, out strSize)) { vm.heap.Read(strPtr, strSize, out var strBytes); @@ -366,12 +351,13 @@ public static void ReadValueString(VirtualMachine vm, string defaultValue, out s break; case TypeCodes.PTR_HEAP: state = CommandArgRuntimeState.HeapRef; - address = MemoryMarshal.Read(span); + address = MemoryMarshal.Read(span); // the heap does not store type info, which means we need to assume the next value on the stack is the type code. typeCode = vm.stack.Pop(); - if (vm.heap.TryGetAllocationSize(address, out strSize)) + var ptrAddress = VmPtr.FromRaw(address); + if (vm.heap.TryGetAllocationSize(ptrAddress, out strSize)) { - vm.heap.Read(address, strSize, out var strBytes); + vm.heap.Read(ptrAddress, strSize, out var strBytes); value = VmConverter.ToString(strBytes); } else @@ -383,7 +369,9 @@ public static void ReadValueString(VirtualMachine vm, string defaultValue, out s default: state = CommandArgRuntimeState.Value; address = 0; - strPtr = MemoryMarshal.Read(span); + var rawData = MemoryMarshal.Read(span); + // strPtr = MemoryMarshal.Read(span); + strPtr = VmPtr.FromRaw(rawData); if (vm.heap.TryGetAllocationSize(strPtr, out strSize)) { vm.heap.Read(strPtr, strSize, out var strBytes); @@ -397,7 +385,7 @@ public static void ReadValueString(VirtualMachine vm, string defaultValue, out s } } - public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, out CommandArgRuntimeState state, out int address) where T : struct + public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, out CommandArgRuntimeState state, out ulong address) where T : struct { ReadSpan(ref vm.stack, out var typeCode, out var span); switch (typeCode) @@ -409,7 +397,7 @@ public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, break; case TypeCodes.PTR_REG: state = CommandArgRuntimeState.RegisterRef; - address = (int) span[0]; + address = MemoryMarshal.Read(span); var data = vm.dataRegisters[address]; typeCode = vm.typeRegisters[address]; var bytes = BitConverter.GetBytes(data); @@ -417,7 +405,7 @@ public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, break; case TypeCodes.PTR_GLOBAL_REG: state = CommandArgRuntimeState.GlobalRegisterRef; - address = (int) span[0]; + address = MemoryMarshal.Read(span); data = vm.globalScope.dataRegisters[address]; typeCode = vm.globalScope.typeRegisters[address]; bytes = BitConverter.GetBytes(data); @@ -425,7 +413,7 @@ public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, break; case TypeCodes.PTR_HEAP: state = CommandArgRuntimeState.HeapRef; - address = MemoryMarshal.Read(span); + address = MemoryMarshal.Read(span); // the heap does not store type info, which means we need to assume the next value on the stack is the type code. typeCode = vm.stack.Pop(); @@ -435,7 +423,7 @@ public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, { var size = TypeCodes.GetByteSize(typeCode); // vm.heap.Read(address, size, out bytes); - vm.heap.ReadSpan(address, size, out span); + vm.heap.ReadSpan(VmPtr.FromRaw(address), size, out span); } value = MemoryMarshal.Read(span); @@ -449,7 +437,7 @@ public static void ReadValue(VirtualMachine vm, T defaultValue, out T value, } - public static void ReadValueAny(VirtualMachine vm, object defaultValue, out object value, out CommandArgRuntimeState state, out int address) + public static void ReadValueAny(VirtualMachine vm, object defaultValue, out object value, out CommandArgRuntimeState state, out ulong address) { // peek the type code... var peekTypeCode = vm.stack.Peek(); @@ -497,44 +485,37 @@ public static void WriteToHeap(ref FastStack stack, ref VmHeap heap, bool { // ReadAsInt(stack, out var writePtr); var typeCode = stack.Pop(); - if (typeCode != TypeCodes.INT) + if (typeCode != TypeCodes.PTR_HEAP && typeCode != TypeCodes.STRUCT) // TODO: this used to say INT. Maybe it needs to also allow STRING? { - throw new Exception("vm exception: expected an int for ptr"); + throw new Exception("vm exception: expected ptr"); } - ReadSpan(ref stack, TypeCodes.INT, out var aSpan); + ReadSpan(ref stack, TypeCodes.PTR_HEAP, out var aSpan); - // Read(stack, TypeCodes.INT, out var aBytes); - var writePtr = BitConverter.ToInt32(aSpan.ToArray(), 0); + var writePtr = VmPtr.FromBytes(aSpan); ReadAsInt(ref stack, out var writeLength); // var bytes = new byte[writeLength]; stack.PopArraySpan(writeLength, out var span); - /* - * In the old system, this just happened to work by accident. - * but the issue is that the stack has 3,0,0,0 on it, - * but the write-length is set to the size of the entry, which is 2. - * so the write-length only finds 0,0 instead of 0,3 - * - * Or I guess, we could CAST the type to the desired type... - */ - - // for (var w = 0; w < writeLength; w++) - // { - // var b = stack.Pop(); - // bytes[w] = b; - // } - - //heap.Write(writePtr, writeLength, span.ToArray()); heap.WriteSpan(writePtr, writeLength, span); if (pushPtr) { - PushSpan(ref stack, aSpan, TypeCodes.INT); - // Push(stack, aBytes, TypeCodes.INT); + PushSpan(ref stack, aSpan, TypeCodes.PTR_HEAP); + } + } + + public static void ReadAsVmPtr(ref FastStack stack, out VmPtr result) + { + var typeCode = stack.Pop(); + if (typeCode != TypeCodes.PTR_HEAP && typeCode != TypeCodes.STRUCT) // TODO: does this also need to include string? + { + throw new Exception("vm exception: expected a ptr"); } + ReadSpan(ref stack, TypeCodes.PTR_HEAP, out var span); + result = VmPtr.FromBytes(span); } public static void ReadAsInt(ref FastStack stack, out int result) @@ -911,8 +892,8 @@ public static void EqualTo(ref FastStack stack, ref VmHeap heap, byte aTyp // read the data at a // VmUtil.ReadAsInt(); - var aPtr = BitConverter.ToInt32(a, 0); - var bPtr = BitConverter.ToInt32(b, 0); + var aPtr = VmPtr.FromBytes(a); + var bPtr = VmPtr.FromBytes(b); heap.GetAllocationSize(aPtr, out var aSize); heap.GetAllocationSize(bPtr, out var bSize); @@ -1242,13 +1223,31 @@ public static void Add(ref VmHeap heap, byte aTypeCode, ReadOnlySpan aSpan byte[] b = bSpan.ToArray(); switch (aTypeCode) { + case TypeCodes.STRUCT: + case TypeCodes.PTR_HEAP: + var ptrA = VmPtr.FromBytes(a); + var ptrB = VmPtr.FromBytes(b); + if (ptrA.bucketPtr != 0) + { + // the second pointer must always have a zero bucket, because the final pointer must be + // in the same bucket as the first pointer. + throw new InvalidOperationException("cannot add pointer buckets"); + } + + var ptrC = new VmPtr + { + bucketPtr = ptrA.bucketPtr, + memoryPtr = ptrA.memoryPtr + ptrB.memoryPtr + }; + c = VmPtr.GetBytes(ref ptrC); + break; case TypeCodes.STRING: /* * two pointers; we need to allocate a new string for the sum of the lengths; * then array copy the two strings into their respective positions */ - var aPtr = BitConverter.ToInt32(a, 0); - var bPtr = BitConverter.ToInt32(b, 0); + var aPtr = VmPtr.FromBytes(a); + var bPtr = VmPtr.FromBytes(b); heap.GetAllocationSize(aPtr, out var aLength); heap.GetAllocationSize(bPtr, out var bLength); var sumLength = aLength + bLength; @@ -1259,7 +1258,7 @@ public static void Add(ref VmHeap heap, byte aTypeCode, ReadOnlySpan aSpan heap.Copy(bPtr, sumPtr, bLength); heap.Copy(aPtr, sumPtr + bLength, aLength); - c = BitConverter.GetBytes(sumPtr); + c = VmPtr.GetBytes(ref sumPtr); break; case TypeCodes.BOOL: case TypeCodes.BYTE: @@ -1402,8 +1401,13 @@ public static void CastInlineSpan(ReadOnlySpan span, byte currentTypeCode, case TypeCodes.PTR_HEAP: case TypeCodes.STRUCT: case TypeCodes.STRING: - // a string type IS just an int ptr; so we don't need to convert anything! - outputSpan = span; + // assume that int pointers are all in the same bucket + var mem = BitConverter.ToInt32(span.ToArray(), 0); + var ptr = new VmPtr + { + memoryPtr = mem + }; + outputSpan = VmPtr.GetBytes(ref ptr); break; default: throw new NotImplementedException($"cast from int to typeCode=[{typeCode}] is not supported yet."); @@ -1525,13 +1529,32 @@ public static void CastInlineSpan(ReadOnlySpan span, byte currentTypeCode, case TypeCodes.STRUCT: switch (typeCode) { - case TypeCodes.INT: + case TypeCodes.PTR_HEAP: outputSpan = span; break; default: throw new NotImplementedException($"cast from struct ptr to typeCode=[{typeCode}] is not supported yet."); } + break; + case TypeCodes.PTR_HEAP: + switch (typeCode) + { + // case TypeCodes.INT: + // var intVersion = BitConverter.ToInt32(span.ToArray(), 0); + // var ptr = new VmPtr + // { + // memoryPtr = intVersion + // }; + // outputSpan = VmPtr.GetBytes(ref ptr); + // break; + case TypeCodes.STRUCT: + case TypeCodes.STRING: + outputSpan = span; + break; + default: + throw new NotImplementedException($"cast from ptr to typeCode=[{typeCode}] is not supported yet."); + } break; default: throw new NotImplementedException($"casts from typeCode=[{currentTypeCode}] types are not supported. target=[{typeCode}]"); @@ -1618,5 +1641,11 @@ public static void GetMinMax(byte aTypeCode, ReadOnlySpan aSpan, ReadOnlyS throw new Exception("Unsupported add operation"); } } + + public static void ReadRegAddress(byte[] program, ref int instructionIndex, out ulong addr) + { + addr = BitConverter.ToUInt64(program, instructionIndex); + instructionIndex += sizeof(ulong); + } } } \ No newline at end of file diff --git a/FadeBasic/FadeCommandsViaNuget/FadeCommandsViaNuget.csproj b/FadeBasic/FadeCommandsViaNuget/FadeCommandsViaNuget.csproj index ac5f981..cbfb958 100644 --- a/FadeBasic/FadeCommandsViaNuget/FadeCommandsViaNuget.csproj +++ b/FadeBasic/FadeCommandsViaNuget/FadeCommandsViaNuget.csproj @@ -5,7 +5,7 @@ enable enable $(FADE_NUGET_VERSION) - 0.13.12.293 + 0.12.13.305 diff --git a/FadeBasic/Tests/DebuggerTests.cs b/FadeBasic/Tests/DebuggerTests.cs index 01517bc..d84f885 100644 --- a/FadeBasic/Tests/DebuggerTests.cs +++ b/FadeBasic/Tests/DebuggerTests.cs @@ -380,7 +380,7 @@ public async Task DebugServerTest() session.StartDebugging(2); // read the message (1 op for the read, and 1 op to move the debugger forward) await Task.Delay(100); // fluff time for the ack to emit Assert.That(receivedConf, Is.True); - Assert.That(session.InstructionPointer, Is.EqualTo(20), + Assert.That(session.InstructionPointer, Is.EqualTo(27), "The debugger should be paused, so the insptr should not have moved from last time."); } diff --git a/FadeBasic/Tests/Fixtures/Projects/UsesProject/usesProject.csproj b/FadeBasic/Tests/Fixtures/Projects/UsesProject/usesProject.csproj index feab418..5b4860e 100644 --- a/FadeBasic/Tests/Fixtures/Projects/UsesProject/usesProject.csproj +++ b/FadeBasic/Tests/Fixtures/Projects/UsesProject/usesProject.csproj @@ -3,7 +3,7 @@ net8.0 Exe $(FADE_NUGET_VERSION) - 0.13.12.293 + 0.12.13.305 diff --git a/FadeBasic/Tests/FunctionVmTests.cs b/FadeBasic/Tests/FunctionVmTests.cs index 2fe548d..ec13337 100644 --- a/FadeBasic/Tests/FunctionVmTests.cs +++ b/FadeBasic/Tests/FunctionVmTests.cs @@ -227,7 +227,7 @@ EndFunction a + ""hello"" var vm = new VirtualMachine(prog); vm.Execute2(); - vm.heap.Read((int)vm.dataRegisters[0], "worldhello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "worldhello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("worldhello")); @@ -251,7 +251,7 @@ EndFunction a$ var vm = new VirtualMachine(prog); vm.Execute2(); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); @@ -288,15 +288,15 @@ EndFunction e var vm = new VirtualMachine(prog); vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4 * 3)); // size of the only field in egg, int, 4. And there are 3 copies; one in global scope, one passed to the function, and one returned from the function + Assert.That(vm.heap.Cursor, Is.EqualTo((4 * 3).ToPtr())); // size of the only field in egg, int, 4. And there are 3 copies; one in global scope, one passed to the function, and one returned from the function Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[0], 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(1)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[1], 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), 4, out memory); data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -436,8 +436,8 @@ EndFunction returnValue$ // at this point, the pointer to the right heap is getting lost??? var expected = "the eight of pie"; - vm.heap.GetAllocationSize((int)vm.dataRegisters[0], out var allocSize); - vm.heap.Read((int)vm.dataRegisters[0], allocSize, out var memory); + vm.heap.GetAllocationSize(vm.dataRegisters[0].ToPtr(), out var allocSize); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), allocSize, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo(expected)); @@ -472,11 +472,11 @@ EndFunction a.x + a.y var vm = new VirtualMachine(prog); vm.Execute().MoveNext(); - vm.heap.Read((int)vm.dataRegisters[0], 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(32)); - vm.heap.Read((int)vm.dataRegisters[0] + 4, 4, out memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 4, 4, out memory); data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(66)); @@ -513,11 +513,11 @@ Function Test(a as egg) var vm = new VirtualMachine(prog); vm.Execute().MoveNext(); - vm.heap.Read((int)vm.dataRegisters[0], 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(32)); - vm.heap.Read((int)vm.dataRegisters[0] + 4, 4, out memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 4, 4, out memory); data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(66)); diff --git a/FadeBasic/Tests/TestExtensions.cs b/FadeBasic/Tests/TestExtensions.cs new file mode 100644 index 0000000..6e23756 --- /dev/null +++ b/FadeBasic/Tests/TestExtensions.cs @@ -0,0 +1,14 @@ +using FadeBasic.Virtual; + +namespace Tests; + +public static class TestExtensions +{ + public static VmPtr ToPtr(this ulong raw) => VmPtr.FromRaw(raw); + + public static VmPtr ToPtr(this int raw) => new VmPtr + { + bucketPtr = 0, + memoryPtr = raw + }; +} \ No newline at end of file diff --git a/FadeBasic/Tests/TokenVm.cs b/FadeBasic/Tests/TokenVm.cs index fb18360..b012216 100644 --- a/FadeBasic/Tests/TokenVm.cs +++ b/FadeBasic/Tests/TokenVm.cs @@ -150,18 +150,18 @@ public void String_Init_StartEmpty() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - Assert.That(vm.dataRegisters[1], Is.EqualTo(1)); // the ptr to the string in memory + Assert.That(vm.dataRegisters[1].ToPtr(), Is.EqualTo(1.ToPtr())); // the ptr to the string in memory Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRING)); Assert.That(vm.typeRegisters[2], Is.EqualTo(TypeCodes.INT)); Assert.That(vm.dataRegisters[2], Is.EqualTo(0)); // zero length string - vm.heap.Read((int)vm.dataRegisters[0], "".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("")); - vm.heap.Read((int)vm.dataRegisters[1], "a".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), "a".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("a")); @@ -183,16 +183,16 @@ public void String_Concat() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); - vm.heap.Read((int)vm.dataRegisters[1], "world".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), "world".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("world")); - vm.heap.Read((int)vm.dataRegisters[2], "helloworld".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[2].ToPtr(), "helloworld".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("helloworld")); } @@ -210,10 +210,10 @@ public void String_Concat_SelfReference() var vm = new VirtualMachine(prog); vm.Execute2(); - Assert.That(vm.dataRegisters[0], Is.EqualTo("world".Length * 4 + "hello ".Length * 4)); // the ptr to the string in memory + Assert.That(vm.dataRegisters[0].ToPtr(), Is.EqualTo(("world".Length * 4 + "hello ".Length * 4).ToPtr())); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello world".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello world".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello world")); @@ -243,11 +243,11 @@ public void String_Concat_VariableAndLiteral() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); - vm.heap.Read((int)vm.dataRegisters[1], "hello world".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), "hello world".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello world")); @@ -269,11 +269,11 @@ public void String_Concat_LiteralAndVariable() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); - vm.heap.Read((int)vm.dataRegisters[1], "helloworld".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), "helloworld".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("worldhello")); @@ -292,7 +292,7 @@ public void String_Declare2_WithIncrementalEval() Assert.That(vm.dataRegisters[1], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read(0, "hello".Length * 4, out var memory); + vm.heap.Read(0.ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); @@ -310,7 +310,7 @@ public void String_Declare() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read(0, "hello".Length * 4, out var memory); + vm.heap.Read(0.ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); @@ -349,7 +349,7 @@ public void String_Declare_HugeHeap() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read(0, initialStr.Length * 4, out var memory); + vm.heap.Read(0.ToPtr(), initialStr.Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo(initialStr)); @@ -368,7 +368,7 @@ public void String_Declare_Anon() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read(0, "hello".Length * 4, out var memory); + vm.heap.Read(0.ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); @@ -384,16 +384,16 @@ public void String_Declare3() Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - Assert.That(vm.dataRegisters[1], Is.EqualTo("hello".Length*4)); // the ptr to the string in memory + Assert.That(vm.dataRegisters[1].ToPtr(), Is.EqualTo(("hello".Length*4).ToPtr())); // the ptr to the string in memory Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read(0, "hello".Length * 4, out var memory); + vm.heap.Read(0.ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); - vm.heap.Read("hello".Length * 4, "world".Length * 4, out memory); + vm.heap.Read(("hello".Length * 4).ToPtr(), "world".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("world")); @@ -1818,7 +1818,7 @@ y as byte var vm = new VirtualMachine(prog); vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4)); + Assert.That(vm.heap.Cursor, Is.EqualTo(4.ToPtr())); var heap = vm.heap; Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); @@ -1844,7 +1844,7 @@ y as byte var vm = new VirtualMachine(prog); vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4)); + Assert.That(vm.heap.Cursor, Is.EqualTo(4.ToPtr())); Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); @@ -1894,7 +1894,7 @@ dim x(4) var vm = new VirtualMachine(prog); vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); @@ -1915,7 +1915,7 @@ public void Array_Math_Floats() var vm = new VirtualMachine(prog); vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); @@ -1947,7 +1947,7 @@ dim x(4) Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); } @@ -1965,7 +1965,7 @@ dim x(4,2) Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(32)); + Assert.That(vm.heap.Cursor, Is.EqualTo(32.ToPtr())); } @@ -1984,10 +1984,10 @@ dim x(4,2) as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); - vm.heap.Read((1*2 + 1) * 2, 2, out var bytes); + vm.heap.Read(((1*2 + 1) * 2).ToPtr(), 2, out var bytes); var value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(42)); } @@ -2007,10 +2007,10 @@ dim x(4,5,3) as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(120)); + Assert.That(vm.heap.Cursor, Is.EqualTo(120.ToPtr())); - vm.heap.Read(( (2*5*3) + (3*3) + 2) * 2, 2, out var bytes); + vm.heap.Read((( (2*5*3) + (3*3) + 2) * 2).ToPtr(), 2, out var bytes); var value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(42)); } @@ -2033,7 +2033,7 @@ y as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(120)); + Assert.That(vm.heap.Cursor, Is.EqualTo(120.ToPtr())); Assert.That(vm.dataRegisters[1 + (3*2) /* 3 ranks, 2 registers per rank. */], Is.EqualTo(42)); Assert.That(vm.typeRegisters[1 + (3*2)], Is.EqualTo(TypeCodes.WORD)); @@ -2057,7 +2057,7 @@ dim x(4,5,3) as byte Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(60)); + Assert.That(vm.heap.Cursor, Is.EqualTo(60.ToPtr())); Assert.That(vm.dataRegisters[1 + (3*2) /* 3 ranks, 2 registers per rank. */], Is.EqualTo(53)); Assert.That(vm.typeRegisters[1 + (3*2)], Is.EqualTo(TypeCodes.INT)); @@ -2078,7 +2078,7 @@ dim x(4) as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); } @@ -2123,9 +2123,9 @@ dim x(5) as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(10)); + Assert.That(vm.heap.Cursor, Is.EqualTo(10.ToPtr())); - vm.heap.Read(2, 2, out var bytes); + vm.heap.Read(2.ToPtr(), 2, out var bytes); var value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(12)); } @@ -2147,13 +2147,13 @@ dim x(5) as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(10)); + Assert.That(vm.heap.Cursor, Is.EqualTo(10.ToPtr())); - vm.heap.Read(2, 2, out var bytes); + vm.heap.Read(2.ToPtr(), 2, out var bytes); var value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(12)); - vm.heap.Read(4, 2, out bytes); + vm.heap.Read(4.ToPtr(), 2, out bytes); value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(5)); } @@ -2175,13 +2175,13 @@ dim x(5) as word Assert.That(vm.dataRegisters[0], Is.EqualTo(0)); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.heap.Cursor, Is.EqualTo(10)); + Assert.That(vm.heap.Cursor, Is.EqualTo(10.ToPtr())); - vm.heap.Read(2, 2, out var bytes); + vm.heap.Read(2.ToPtr(), 2, out var bytes); var value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(12)); - vm.heap.Read(4, 2, out bytes); + vm.heap.Read(4.ToPtr(), 2, out bytes); value = BitConverter.ToInt16(bytes, 0); Assert.That(value, Is.EqualTo(24)); } @@ -2573,8 +2573,8 @@ global x Assert.That(vm.globalScope.dataRegisters[1], Is.EqualTo(4)); // register 1 is y, Assert.That(vm.globalScope.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); - Assert.That(vm.dataRegisters[2], Is.EqualTo(0)); // register 2 should have nothing in it - Assert.That(vm.typeRegisters[2], Is.EqualTo(0)); + Assert.That(vm.dataRegisters.Length, Is.EqualTo(3)); // 2, plus 1 for the debugger room + Assert.That(vm.typeRegisters.Length, Is.EqualTo(3)); } @@ -2597,9 +2597,9 @@ public void Declare_InLoop() Assert.That(vm.globalScope.dataRegisters[1], Is.EqualTo(3)); // register 1 is y, Assert.That(vm.globalScope.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); - - Assert.That(vm.dataRegisters[2], Is.EqualTo(0)); // register 2 should have nothing in it - Assert.That(vm.typeRegisters[2], Is.EqualTo(0)); + + Assert.That(vm.dataRegisters.Length, Is.EqualTo(3)); // 2, plus 1 for the debugger room + Assert.That(vm.typeRegisters.Length, Is.EqualTo(3)); } @@ -2816,7 +2816,7 @@ public void Math_NegativeSignFlip() Assert.That(vm.dataRegisters[1], Is.EqualTo(0)); // the ptr to the string in memory Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read(0, "-4".Length * 4, out var memory); + vm.heap.Read(0.ToPtr(), "-4".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("-4")); } @@ -2931,7 +2931,7 @@ a as egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); // size of the only field in egg, int, 4. (times 2, because there are 2 copies) + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); // size of the only field in egg, int, 4. (times 2, because there are 2 copies) Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRUCT)); } @@ -2953,7 +2953,7 @@ y as egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4)); // size of the only field in egg, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(4.ToPtr())); // size of the only field in egg, int, 4. Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); } @@ -2974,10 +2974,10 @@ y as egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4)); // size of the only field in egg, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(4.ToPtr())); // size of the only field in egg, int, 4. Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[0], 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(53)); } @@ -3000,10 +3000,10 @@ y as egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4)); // size of the only field in egg, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(4.ToPtr())); // size of the only field in egg, int, 4. Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[0], 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(106)); } @@ -3028,7 +3028,7 @@ y as egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); @@ -3055,7 +3055,7 @@ y as egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4 + 4 + "hello".Length*4)); // a '4' comes from y.x, and the other '4' is the ptr to z$ + Assert.That(vm.heap.Cursor, Is.EqualTo(12.ToPtr() + ("hello".Length*4))); // egg is 4+8 big (an int plus a pointer), and the size of the string. Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); @@ -3084,7 +3084,7 @@ albert as chicken vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); // Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); @@ -3113,7 +3113,7 @@ albert as chicken vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); // it shouldn't matter the order we define the types, because we are parse-sure they don't have loops. @@ -3148,7 +3148,7 @@ player as object vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); @@ -3186,7 +3186,7 @@ player as object vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); @@ -3218,7 +3218,7 @@ albert AS chicken vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.INT)); @@ -3242,7 +3242,7 @@ DIM x(3) AS egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(12)); + Assert.That(vm.heap.Cursor, Is.EqualTo(12.ToPtr())); // Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); } @@ -3264,7 +3264,7 @@ DIM x(3) AS egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(6)); + Assert.That(vm.heap.Cursor, Is.EqualTo(6.ToPtr())); // Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); } @@ -3286,9 +3286,9 @@ DIM x(3) AS egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(6)); + Assert.That(vm.heap.Cursor, Is.EqualTo(6.ToPtr())); - vm.heap.Read(1 * 2, 2, out var mem); + vm.heap.Read((1 * 2).ToPtr(), 2, out var mem); var data = BitConverter.ToInt16(mem, 0); Assert.That(data, Is.EqualTo(3)); @@ -3313,9 +3313,9 @@ DIM x(3,2) AS egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(12)); + Assert.That(vm.heap.Cursor, Is.EqualTo(12.ToPtr())); - vm.heap.Read((1 + 2 * 1) * 2, 2, out var mem); + vm.heap.Read(((1 + 2 * 1) * 2).ToPtr(), 2, out var mem); var data = BitConverter.ToInt16(mem, 0); Assert.That(data, Is.EqualTo(3)); @@ -3342,7 +3342,7 @@ DIM x(3) AS egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(18)); + Assert.That(vm.heap.Cursor, Is.EqualTo(18.ToPtr())); Assert.That(vm.dataRegisters[3], Is.EqualTo(3)); @@ -3367,7 +3367,7 @@ DIM x(3,4) AS egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(6*12)); + Assert.That(vm.heap.Cursor, Is.EqualTo((6*12).ToPtr())); Assert.That(vm.globalScope.dataRegisters[3], Is.EqualTo(3)); } @@ -3401,7 +3401,7 @@ n as egg vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(6*(3 + 1))); // 1 extra for the original assignment of n as egg + Assert.That(vm.heap.Cursor, Is.EqualTo((6*(3 + 1)).ToPtr())); // 1 extra for the original assignment of n as egg Assert.That(vm.dataRegisters[0], Is.EqualTo(122)); } @@ -3426,7 +3426,7 @@ DIM x(3) AS egg vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(18)); + Assert.That(vm.heap.Cursor, Is.EqualTo(18.ToPtr())); Assert.That(vm.dataRegisters[3], Is.EqualTo(6)); @@ -3534,10 +3534,10 @@ dim bread(2) as toast var vm = new VirtualMachine(prog); vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); // size of the only field in toast, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); // size of the only field in toast, int, 4. // Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.PTR_HEAP)); - vm.heap.Read((int)vm.dataRegisters[0] + 4, 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 4, 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -3557,10 +3557,10 @@ bread as toast var vm = new VirtualMachine(prog); vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(4)); // size of the only field in toast, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(4.ToPtr())); // size of the only field in toast, int, 4. Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[0], 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -3582,10 +3582,10 @@ dim bread(2) as toast var vm = new VirtualMachine(prog); vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(16)); // size of the only field in toast, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(16.ToPtr())); // size of the only field in toast, int, 4. // Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.PTR_HEAP)); - vm.heap.Read((int)vm.dataRegisters[0] + 12, 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 12, 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -3611,10 +3611,10 @@ dim bread(2) as toast var vm = new VirtualMachine(prog); vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(24)); // size of the only field in toast, int, 4. + Assert.That(vm.heap.Cursor, Is.EqualTo(24.ToPtr())); // size of the only field in toast, int, 4. // Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.PTR_HEAP)); - vm.heap.Read((int)vm.dataRegisters[0] + 20, 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 20, 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -3636,10 +3636,10 @@ bread as toast var vm = new VirtualMachine(prog); vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(8)); // there are two ints, each are 4 + Assert.That(vm.heap.Cursor, Is.EqualTo(8.ToPtr())); // there are two ints, each are 4 Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[0] + 4, 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 4, 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -3667,10 +3667,10 @@ bread as toast var vm = new VirtualMachine(prog); vm.hostMethods = compiler.methodTable; vm.Execute2(); - Assert.That(vm.heap.Cursor, Is.EqualTo(12)); // there are three ints, each are 4 + Assert.That(vm.heap.Cursor, Is.EqualTo(12.ToPtr())); // there are three ints, each are 4 Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRUCT)); - vm.heap.Read((int)vm.dataRegisters[0] + 8, 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr() + 8, 4, out var memory); var data = BitConverter.ToInt32(memory); Assert.That(data, Is.EqualTo(6)); } @@ -3864,7 +3864,7 @@ public void CallHost_Params_Object() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "1;hello;2".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "1;hello;2".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("1;hello;2")); } @@ -3879,7 +3879,7 @@ public void CallHost_RefType_String() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "tuna".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "tuna".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("tuna")); } @@ -3893,7 +3893,7 @@ public void CallHost_RefType_String_2() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "tuna".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "tuna".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("tuna")); } @@ -3968,12 +3968,31 @@ public void CallHost_RefType_String_FromArray() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[3], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[3], "tuna".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[3].ToPtr(), "tuna".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("tuna")); } + [Test] + public void Array_StringAssign() + { + var src = @" +dim x$(3) +x$(1) = ""tuna"" +y$ = x$(1) +"; + + Setup(src, out var compiler, out var prog); + var vm = new VirtualMachine(prog); + vm.hostMethods = compiler.methodTable; + vm.Execute2(); + Assert.That(vm.typeRegisters[3], Is.EqualTo(TypeCodes.STRING)); + vm.heap.Read(vm.dataRegisters[3].ToPtr(), "tuna".Length * 4, out var memory); + var str = VmConverter.ToString(memory); + Assert.That(str, Is.EqualTo("tuna")); + } + [Test] public void CallHost_StringArg() { @@ -4028,12 +4047,12 @@ public void CallHost_StringArg_FromArray() Assert.That(vm.typeRegisters[3], Is.EqualTo(TypeCodes.INT)); Assert.That(vm.dataRegisters[3], Is.EqualTo("hello".Length)); Assert.That(vm.typeRegisters[4], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[4], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[4].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("olleh")); Assert.That(vm.typeRegisters[5], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[5], "hello".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[5].ToPtr(), "hello".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("olleh")); } @@ -4048,7 +4067,7 @@ public void CallHost_StringReturn() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("olleh")); } @@ -4063,7 +4082,7 @@ public void CallHost_StringReturn_2() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "32".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "32".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("32")); } @@ -4077,12 +4096,12 @@ public void CallHost_StringReturn_Assignment() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[1], "hello".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), "hello".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("olleh")); } @@ -4097,12 +4116,12 @@ public void CallHost_StringReturn_Expression() vm.hostMethods = compiler.methodTable; vm.Execute2(); Assert.That(vm.typeRegisters[0], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[0], "hello".Length * 4, out var memory); + vm.heap.Read(vm.dataRegisters[0].ToPtr(), "hello".Length * 4, out var memory); var str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("hello")); Assert.That(vm.typeRegisters[1], Is.EqualTo(TypeCodes.STRING)); - vm.heap.Read((int)vm.dataRegisters[1], "helloworld".Length * 4, out memory); + vm.heap.Read(vm.dataRegisters[1].ToPtr(), "helloworld".Length * 4, out memory); str = VmConverter.ToString(memory); Assert.That(str, Is.EqualTo("worldolleh")); } diff --git a/FadeBasic/Tests/TokenVm_BigSrc.cs b/FadeBasic/Tests/TokenVm_BigSrc.cs new file mode 100644 index 0000000..28565ed --- /dev/null +++ b/FadeBasic/Tests/TokenVm_BigSrc.cs @@ -0,0 +1,67 @@ +using System.Text; +using FadeBasic.Virtual; + +namespace Tests; + +public partial class TokenVm +{ + + [Test] + public void BigSrc_GiantHeap() + { + var src = @" +TYPE egg + x#, y# +ENDTYPE +e as egg +e.x# = 1.2 +e.y# = 3.4 + +"; + Setup(src, out var compiler, out var prog); + var vm = new VirtualMachine(prog); + vm.hostMethods = compiler.methodTable; + + // allocation a bunch of memory + vm.heap.Allocate(ref HeapTypeFormat.STRING_FORMAT, (int)VmPtr.MAX_ARRAY_SIZE - 1, out var dumbPtr); + + vm.Execute2(0); + + var expectedPtr = new VmPtr + { + bucketPtr = 1, + memoryPtr = 0 + }; + if (!vm.heap.TryGetAllocation(expectedPtr, out var allocation)) + { + Assert.Fail("pointer is not at expected location"); + } + + Assert.That(allocation.length, Is.EqualTo(8)); // 2 floats + } + + + [Test] + public void BigSrc_LotsOfVariables() + { + var sb = new StringBuilder(); + var max = short.MaxValue / 2; // this number cannot get to big, because the string-builder explodes. + for (int i = 0; i < max; i++) + { + sb.AppendLine($"x_{i} = {i}"); + } + + var src = sb.ToString(); + Setup(src, out var compiler, out var prog); + var vm = new VirtualMachine(prog); + vm.hostMethods = compiler.methodTable; + vm.Execute2(0); + + for (int i = 0; i < max; i++) + { + Assert.That(vm.dataRegisters[i], Is.EqualTo(i), $"variable at reg {i} is wrong."); + + } + } + +} \ No newline at end of file diff --git a/FadeBasic/Tests/TokenVm_GC.cs b/FadeBasic/Tests/TokenVm_GC.cs index 52ca15e..b5c0ba6 100644 --- a/FadeBasic/Tests/TokenVm_GC.cs +++ b/FadeBasic/Tests/TokenVm_GC.cs @@ -113,7 +113,7 @@ public void String_Interning(string str, string expected) Assert.That(xPtr, Is.EqualTo(0)); Assert.That(yPtr, Is.EqualTo(xPtr)); - vm.heap.Read((int)xPtr, expected.Length * 4, out var memory); + vm.heap.Read(xPtr.ToPtr(), expected.Length * 4, out var memory); var actual = VmConverter.ToString(memory); Assert.That(actual, Is.EqualTo(expected));