diff --git a/samples/Snake/FrameBuffer.cs b/samples/Snake/FrameBuffer.cs new file mode 100644 index 0000000..580fdad --- /dev/null +++ b/samples/Snake/FrameBuffer.cs @@ -0,0 +1,45 @@ +using System; + +unsafe struct FrameBuffer +{ + public const int Width = 40; + public const int Height = 20; + public const int Area = Width * Height; + + fixed char _chars[Area]; + + public void SetPixel(int x, int y, char character) + { + _chars[y * Width + x] = character; + } + + public void Clear() + { + for (int i = 0; i < Area; i++) + _chars[i] = ' '; + } + + public readonly void Render() + { + const ConsoleColor snakeColor = ConsoleColor.Green; + + Console.ForegroundColor = snakeColor; + + for (int i = 0; i < Area; i++) + { + if (i % Width == 0) + Console.SetCursorPosition(0, i / Width); + + char c = _chars[i]; + + if (c == '*' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + Console.ForegroundColor = c == '*' ? ConsoleColor.Red : ConsoleColor.White; + Console.Write(c); + Console.ForegroundColor = snakeColor; + } + else + Console.Write(c); + } + } +} diff --git a/samples/Snake/Game.cs b/samples/Snake/Game.cs new file mode 100644 index 0000000..4763bcb --- /dev/null +++ b/samples/Snake/Game.cs @@ -0,0 +1,119 @@ +using System; +using System.Runtime.InteropServices; +using Thread = System.Threading.Thread; + +struct Game +{ + enum Result + { + Win, Loss + } + + private Random _random; + + private Game(uint randomSeed) + { + _random = new Random(randomSeed); + } + + private Result Run(ref FrameBuffer fb) + { + Snake s = new Snake( + (byte)(_random.Next() % FrameBuffer.Width), + (byte)(_random.Next() % FrameBuffer.Height), + (Snake.Direction)(_random.Next() % 4)); + + MakeFood(s, out byte foodX, out byte foodY); + + long gameTime = Environment.TickCount64; + + while (true) + { + fb.Clear(); + + if (!s.Update()) + { + s.Draw(ref fb); + return Result.Loss; + } + + s.Draw(ref fb); + + if (Console.KeyAvailable) + { + ConsoleKeyInfo ki = Console.ReadKey(intercept: true); + switch (ki.Key) + { + case ConsoleKey.UpArrow: + s.Course = Snake.Direction.Up; break; + case ConsoleKey.DownArrow: + s.Course = Snake.Direction.Down; break; + case ConsoleKey.LeftArrow: + s.Course = Snake.Direction.Left; break; + case ConsoleKey.RightArrow: + s.Course = Snake.Direction.Right; break; + } + } + + if (s.HitTest(foodX, foodY)) + { + if (s.Extend()) + MakeFood(s, out foodX, out foodY); + else + return Result.Win; + } + + fb.SetPixel(foodX, foodY, '*'); + + fb.Render(); + + gameTime += 100; + + long delay = gameTime - Environment.TickCount64; + if (delay >= 0) + Thread.Sleep((int)delay); + else + gameTime = Environment.TickCount64; + } + } + + void MakeFood(in Snake snake, out byte foodX, out byte foodY) + { + do + { + foodX = (byte)(_random.Next() % FrameBuffer.Width); + foodY = (byte)(_random.Next() % FrameBuffer.Height); + } + while (snake.HitTest(foodX, foodY)); + } + + public static void Main() + { +#if WINDOWS + Console.SetWindowSize(FrameBuffer.Width, FrameBuffer.Height + 1); + Console.SetBufferSize(FrameBuffer.Width, FrameBuffer.Height + 1); + Console.Title = "See Sharp Snake"; + Console.CursorVisible = false; +#endif + + FrameBuffer fb = new FrameBuffer(); + + while (true) + { + Game g = new Game((uint)Environment.TickCount64); + Result result = g.Run(ref fb); + + string message = result == Result.Win ? "You win" : "You lose"; + + int position = (FrameBuffer.Width - message.Length) / 2; + for (int i = 0; i < message.Length; i++) + { + fb.SetPixel(position + i, FrameBuffer.Height / 2, message[i]); + } + + fb.Render(); + + Console.ReadKey(intercept: true); + } + } +} diff --git a/samples/Snake/README.md b/samples/Snake/README.md new file mode 100644 index 0000000..dfc18f9 --- /dev/null +++ b/samples/Snake/README.md @@ -0,0 +1,19 @@ +# ZeroLib sample + +Basides the .NET standard library, bflat also comes with a minimal standard library that is an very minimal subset of what's in .NET. + +There is no garbage collector. No exception handling. No useful collection types. Just a couple things to run at least _some_ code. + +Think of it more as an art project than something useful. Building something that fits the supported envelope is more an artistic expression than anything useful. + +This directory contains a sample app (a snake game) that can be compiled both in the standard way (with a useful standard library) or with zerolib. + +To build the sample with zerolib, run: + +```console +$ bflat build --stdlib:zero +``` + +Most other `build` arguments still apply, so you can make things smaller with e.g. `--separate-symbols --no-pie`, or you can crosscompile with `--os:windows` and `--os:linux`. + +You should see a fully selfcontained executable that is 10-50 kB in size, depending on the platform. That's the "art" part. diff --git a/samples/Snake/Random.cs b/samples/Snake/Random.cs new file mode 100644 index 0000000..e8a6d2a --- /dev/null +++ b/samples/Snake/Random.cs @@ -0,0 +1,11 @@ +struct Random +{ + private uint _val; + + public Random(uint seed) + { + _val = seed; + } + + public uint Next() => _val = (1103515245 * _val + 12345) % 2147483648; +} \ No newline at end of file diff --git a/samples/Snake/Snake.cs b/samples/Snake/Snake.cs new file mode 100644 index 0000000..1fb5c90 --- /dev/null +++ b/samples/Snake/Snake.cs @@ -0,0 +1,137 @@ +struct Snake +{ + public const int MaxLength = 30; + + private int _length; + + // Body is a packed integer that packs the X coordinate, Y coordinate, and the character + // for the snake's body. + // Only primitive types can be used with C# `fixed`, hence this is an `int`. + private unsafe fixed int _body[MaxLength]; + + private Direction _direction; + private Direction _oldDirection; + + public Direction Course + { + set + { + if (_oldDirection != _direction) + _oldDirection = _direction; + + if (_direction - value != 2 && value - _direction != 2) + _direction = value; + } + } + + public unsafe Snake(byte x, byte y, Direction direction) + { + _body[0] = new Part(x, y, DirectionToChar(direction, direction)).Pack(); + _direction = direction; + _oldDirection = direction; + _length = 1; + } + + public unsafe bool Update() + { + Part oldHead = Part.Unpack(_body[0]); + Part newHead = new Part( + (byte)(_direction switch + { + Direction.Left => oldHead.X == 0 ? FrameBuffer.Width - 1 : oldHead.X - 1, + Direction.Right => (oldHead.X + 1) % FrameBuffer.Width, + _ => oldHead.X, + }), + (byte)(_direction switch + { + Direction.Up => oldHead.Y == 0 ? FrameBuffer.Height - 1 : oldHead.Y - 1, + Direction.Down => (oldHead.Y + 1) % FrameBuffer.Height, + _ => oldHead.Y, + }), + DirectionToChar(_direction, _direction) + ); + + oldHead = new Part(oldHead.X, oldHead.Y, DirectionToChar(_oldDirection, _direction)); + + bool result = true; + + for (int i = 0; i < _length - 1; i++) + { + Part current = Part.Unpack(_body[i]); + if (current.X == newHead.X && current.Y == newHead.Y) + result = false; + } + + _body[0] = oldHead.Pack(); + + for (int i = _length - 2; i >= 0; i--) + { + _body[i + 1] = _body[i]; + } + + _body[0] = newHead.Pack(); + + _oldDirection = _direction; + + return result; + } + + public unsafe readonly void Draw(ref FrameBuffer fb) + { + for (int i = 0; i < _length; i++) + { + Part p = Part.Unpack(_body[i]); + fb.SetPixel(p.X, p.Y, p.Character); + } + } + + public bool Extend() + { + if (_length < MaxLength) + { + _length += 1; + return true; + } + return false; + } + + public unsafe readonly bool HitTest(int x, int y) + { + for (int i = 0; i < _length; i++) + { + Part current = Part.Unpack(_body[i]); + if (current.X == x && current.Y == y) + return true; + } + + return false; + } + + private static char DirectionToChar(Direction oldDirection, Direction newDirection) + { + const string DirectionChangeToChar = "│┌?┐┘─┐??└│┘└?┌─"; + return DirectionChangeToChar[(int)oldDirection * 4 + (int)newDirection]; + } + + // Helper struct to pack and unpack the packed integer in _body. + readonly struct Part + { + public readonly byte X, Y; + public readonly char Character; + + public Part(byte x, byte y, char c) + { + X = x; + Y = y; + Character = c; + } + + public int Pack() => X << 24 | Y << 16 | Character; + public static Part Unpack(int packed) => new Part((byte)(packed >> 24), (byte)(packed >> 16), (char)packed); + } + + public enum Direction + { + Up, Right, Down, Left + } +} diff --git a/src/bflat/BuildCommand.cs b/src/bflat/BuildCommand.cs index 05e4b86..0a6d6d1 100644 --- a/src/bflat/BuildCommand.cs +++ b/src/bflat/BuildCommand.cs @@ -114,7 +114,7 @@ public static Command Create() DirectPInvokesOption, FeatureSwitchOption, CommonOptions.ResourceOption, - CommonOptions.BareOption, + CommonOptions.StdLibOption, CommonOptions.DeterministicOption, CommonOptions.VerbosityOption, }; @@ -153,11 +153,11 @@ public override int Handle(ParseResult result) else if (nooptimize) optimizationMode = OptimizationMode.None; - bool bare = result.GetValueForOption(CommonOptions.BareOption); + StandardLibType stdlib = result.GetValueForOption(CommonOptions.StdLibOption); string[] userSpecifiedInputFiles = result.GetValueForArgument(CommonOptions.InputFilesArgument); string[] inputFiles = CommonOptions.GetInputFiles(userSpecifiedInputFiles); string[] defines = result.GetValueForOption(CommonOptions.DefinedSymbolsOption); - string[] references = CommonOptions.GetReferencePaths(result.GetValueForOption(CommonOptions.ReferencesOption), bare); + string[] references = CommonOptions.GetReferencePaths(result.GetValueForOption(CommonOptions.ReferencesOption), stdlib); TargetOS targetOS; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -316,10 +316,20 @@ public override int Handle(ParseResult result) targetArchitecture); bool disableReflection = result.GetValueForOption(NoReflectionOption); - bool disableStackTraceData = result.GetValueForOption(NoStackTraceDataOption) || bare; + bool disableStackTraceData = result.GetValueForOption(NoStackTraceDataOption) || stdlib != StandardLibType.DotNet; string systemModuleName = DefaultSystemModule; - if (bare && references.Length == 0) + if (stdlib == StandardLibType.None && references.Length == 0) systemModuleName = compiledModuleName; + if (stdlib == StandardLibType.Zero) + systemModuleName = "zerolib"; + + if (stdlib != StandardLibType.DotNet) + { + // TODO: need to fix this for `--map` + // SettingsTunnel.EmitGCInfo = false; + SettingsTunnel.EmitEHInfo = false; + SettingsTunnel.EmitGSCookies = false; + } bool supportsReflection = !disableReflection && systemModuleName == DefaultSystemModule; @@ -385,9 +395,11 @@ public override int Handle(ParseResult result) } } - if (!bare) + if (stdlib != StandardLibType.None) { - foreach (var reference in EnumerateExpandedDirectories(libPath, "*.dll")) + string mask = stdlib == StandardLibType.DotNet ? "*.dll" : "zerolib.dll"; + + foreach (var reference in EnumerateExpandedDirectories(libPath, mask)) { string assemblyName = Path.GetFileNameWithoutExtension(reference); referenceFilePaths[assemblyName] = reference; @@ -419,7 +431,7 @@ public override int Handle(ParseResult result) // Build a list of assemblies that have an initializer that needs to run before // any user code runs. List assembliesWithInitalizers = new List(); - if (!bare) + if (stdlib == StandardLibType.DotNet) { foreach (string initAssemblyName in initAssemblies) { @@ -473,6 +485,7 @@ public override int Handle(ParseResult result) directPinvokes.Add("System.IO.Compression.Native"); directPinvokes.Add("System.Globalization.Native"); directPinvokes.Add("sokol"); + directPinvokes.Add("shell32!CommandLineToArgvW"); // zerolib uses this } else { @@ -754,7 +767,7 @@ public override int Handle(ParseResult result) if (buildTargetType is BuildTargetType.Exe or BuildTargetType.WinExe) { - if (!bare) + if (stdlib == StandardLibType.DotNet) ldArgs.Append("/entry:wmainCRTStartup bootstrapper.lib "); else ldArgs.Append("/entry:__managed__Main "); @@ -765,7 +778,7 @@ public override int Handle(ParseResult result) else if (buildTargetType is BuildTargetType.Shared) { ldArgs.Append("/dll "); - if (!bare) + if (stdlib == StandardLibType.DotNet) ldArgs.Append("/include:NativeAOT_StaticInitialization bootstrapperdll.lib "); ldArgs.Append($"/def:\"{exportsFile}\" "); } @@ -773,7 +786,7 @@ public override int Handle(ParseResult result) ldArgs.Append("/incremental:no "); if (debugInfoFormat != 0) ldArgs.Append("/debug "); - if (!bare) + if (stdlib == StandardLibType.DotNet) { ldArgs.Append("Runtime.WorkstationGC.lib System.IO.Compression.Native.Aot.lib System.Globalization.Native.Aot.lib "); } @@ -826,7 +839,7 @@ public override int Handle(ParseResult result) ldArgs.Append("-dynamic-linker /lib64/ld-linux-x86-64.so.2 "); ldArgs.Append($"\"{firstLib}/Scrt1.o\" "); } - if (bare) + if (stdlib != StandardLibType.DotNet) ldArgs.Append("--defsym=main=__managed__Main "); } else @@ -854,7 +867,7 @@ public override int Handle(ParseResult result) if (buildTargetType == BuildTargetType.Shared) { - if (!bare) + if (stdlib == StandardLibType.DotNet) { ldArgs.Append("-lbootstrapperdll "); ldArgs.Append("--undefined=NativeAOT_StaticInitialization "); @@ -865,18 +878,22 @@ public override int Handle(ParseResult result) } else { - if (!bare) + if (stdlib == StandardLibType.DotNet) ldArgs.Append("-lbootstrapper "); if (!result.GetValueForOption(NoPieOption)) ldArgs.Append("-pie "); } - if (!bare) + if (stdlib != StandardLibType.None) { - ldArgs.Append("-lstdc++compat -lRuntime.WorkstationGC -lSystem.Native -lSystem.IO.Compression.Native -lSystem.Security.Cryptography.Native.OpenSsl "); - if (libc != "bionic") - ldArgs.Append("-lSystem.Globalization.Native -lSystem.Net.Security.Native "); + ldArgs.Append("-lSystem.Native "); + if (stdlib == StandardLibType.DotNet) + { + ldArgs.Append("-lstdc++compat -lRuntime.WorkstationGC -lSystem.IO.Compression.Native -lSystem.Security.Cryptography.Native.OpenSsl "); + if (libc != "bionic") + ldArgs.Append("-lSystem.Globalization.Native -lSystem.Net.Security.Native "); + } } diff --git a/src/bflat/CommonOptions.cs b/src/bflat/CommonOptions.cs index 38849fa..43d7ac7 100644 --- a/src/bflat/CommonOptions.cs +++ b/src/bflat/CommonOptions.cs @@ -34,9 +34,9 @@ internal static class CommonOptions new Option("--verbose", "Enable verbose logging"); - public static Option BareOption = - new Option("--bare", - "Do not include standard library"); + public static Option StdLibOption = + new Option("--stdlib", + "C# standard library to use"); public static Option DeterministicOption = new Option("--deterministic", @@ -92,14 +92,26 @@ public static string[] GetInputFiles(string[] inputFileNames) return Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.cs", SearchOption.AllDirectories).ToArray(); } - public static string[] GetReferencePaths(string[] referencePaths, bool bare) + public static string[] GetReferencePaths(string[] referencePaths, StandardLibType stdlib) { - if (bare) + if (stdlib == StandardLibType.None) return referencePaths; List result = new List(referencePaths); string refPath = Path.Combine(HomePath, "ref"); - result.AddRange(Directory.GetFiles(refPath, "*.dll")); + if (stdlib == StandardLibType.Zero) + { + result.Add(Path.Combine(refPath, "zerolib.dll")); + } + else + { + foreach (var f in Directory.GetFiles(refPath, "*.dll")) + { + if (f.EndsWith("zerolib.dll")) + continue; + result.Add(f); + } + } return result.ToArray(); } @@ -130,4 +142,11 @@ public enum BuildTargetType Exe = 1, WinExe, Shared, -} \ No newline at end of file +} + +public enum StandardLibType +{ + DotNet, + None, + Zero, +} diff --git a/src/bflat/ILBuildCommand.cs b/src/bflat/ILBuildCommand.cs index 27134ea..4b163e4 100644 --- a/src/bflat/ILBuildCommand.cs +++ b/src/bflat/ILBuildCommand.cs @@ -44,7 +44,7 @@ public static Command Create() CommonOptions.InputFilesArgument, CommonOptions.DefinedSymbolsOption, CommonOptions.ReferencesOption, - CommonOptions.BareOption, + CommonOptions.StdLibOption, CommonOptions.DeterministicOption, CommonOptions.VerbosityOption, CommonOptions.OutputOption, @@ -65,7 +65,7 @@ public override int Handle(ParseResult result) string[] defines = result.GetValueForOption(CommonOptions.DefinedSymbolsOption); string[] references = CommonOptions.GetReferencePaths( result.GetValueForOption(CommonOptions.ReferencesOption), - result.GetValueForOption(CommonOptions.BareOption)); + result.GetValueForOption(CommonOptions.StdLibOption)); OptimizationLevel optimizationLevel = result.GetValueForOption(OptimizeOption) ? OptimizationLevel.Release : OptimizationLevel.Debug; diff --git a/src/bflat/bflat.csproj b/src/bflat/bflat.csproj index 3870904..384fe36 100644 --- a/src/bflat/bflat.csproj +++ b/src/bflat/bflat.csproj @@ -10,7 +10,7 @@ - 7.0.0-rtm.22556.1 + 7.0.0-rtm.22567.1 /~https://github.com/bflattened/runtime/releases/download/ 1.3 @@ -214,6 +214,13 @@ WriteOnlyWhenDifferent="true" /> + + + + diff --git a/src/zerolib/Internal/Startup.Unix.cs b/src/zerolib/Internal/Startup.Unix.cs new file mode 100644 index 0000000..63ec03f --- /dev/null +++ b/src/zerolib/Internal/Startup.Unix.cs @@ -0,0 +1,49 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if !WINDOWS + +using System; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerHelpers +{ + unsafe partial class StartupCodeHelpers + { + static int s_argc; + static sbyte** s_argv; + + internal static unsafe void InitializeCommandLineArgs(int argc, sbyte** argv) + { + s_argc = argc; + s_argv = argv; + } + + private static string[] GetMainMethodArguments() + { + string[] args = new string[s_argc - 1]; + for (int i = 1; i < s_argc; ++i) + { + args[i - 1] = new string(s_argv[i]); + } + + return args; + } + } +} + +#endif diff --git a/src/zerolib/Internal/Startup.Windows.cs b/src/zerolib/Internal/Startup.Windows.cs new file mode 100644 index 0000000..9fcfec2 --- /dev/null +++ b/src/zerolib/Internal/Startup.Windows.cs @@ -0,0 +1,54 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if WINDOWS + +using System; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerHelpers +{ + unsafe partial class StartupCodeHelpers + { + internal static unsafe void InitializeCommandLineArgsW(int argc, char** argv) + { + // argc and argv are a lie because CRT didn't start the process on Windows + } + + private static string[] GetMainMethodArguments() + { + int argc; + char** argv = CommandLineToArgvW(GetCommandLineW(), &argc); + + [DllImport("kernel32")] + static extern char* GetCommandLineW(); + + [DllImport("shell32")] + static extern char** CommandLineToArgvW(char* lpCmdLine, int* pNumArgs); + + string[] args = new string[argc - 1]; + for (int i = 1; i < argc; ++i) + { + args[i - 1] = new string(argv[i]); + } + + return args; + } + } +} + +#endif diff --git a/src/zerolib/Internal/Stubs.cs b/src/zerolib/Internal/Stubs.cs new file mode 100644 index 0000000..b57b92c --- /dev/null +++ b/src/zerolib/Internal/Stubs.cs @@ -0,0 +1,149 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime +{ + internal sealed class RuntimeExportAttribute : Attribute + { + public RuntimeExportAttribute(string entry) { } + } + + internal sealed class RuntimeImportAttribute : Attribute + { + public RuntimeImportAttribute(string lib) { } + public RuntimeImportAttribute(string lib, string entry) { } + } + + internal unsafe struct MethodTable + { + internal ushort _usComponentSize; + private ushort _usFlags; + internal uint _uBaseSize; + internal MethodTable* _relatedType; + private ushort _usNumVtableSlots; + private ushort _usNumInterfaces; + private uint _uHashCode; + } +} + +namespace Internal.Runtime.CompilerHelpers +{ + class ThrowHelpers + { + static void ThrowIndexOutOfRangeException() => Environment.FailFast(null); + static void ThrowDivideByZeroException() => Environment.FailFast(null); + } + + // A class that the compiler looks for that has helpers to initialize the + // process. The compiler can gracefully handle the helpers not being present, + // but the class itself being absent is unhandled. Let's add an empty class. + unsafe partial class StartupCodeHelpers + { + // A couple symbols the generated code will need we park them in this class + // for no particular reason. These aid in transitioning to/from managed code. + // Since we don't have a GC, the transition is a no-op. + [RuntimeExport("RhpReversePInvoke")] + static void RhpReversePInvoke(IntPtr frame) { } + [RuntimeExport("RhpReversePInvokeReturn")] + static void RhpReversePInvokeReturn(IntPtr frame) { } + [RuntimeExport("RhpPInvoke")] + static void RhpPInvoke(IntPtr frame) { } + [RuntimeExport("RhpPInvokeReturn")] + static void RhpPInvokeReturn(IntPtr frame) { } + + [RuntimeExport("RhpFallbackFailFast")] + static void RhpFallbackFailFast() { Environment.FailFast(null); } + + [RuntimeExport("RhpNewFast")] + static unsafe void* RhpNewFast(MethodTable* pMT) + { + MethodTable** result = AllocObject(pMT->_uBaseSize); + *result = pMT; + return result; + } + + [RuntimeExport("RhpNewArray")] + static unsafe void* RhpNewArray(MethodTable* pMT, int numElements) + { + if (numElements < 0) + Environment.FailFast(null); + + MethodTable** result = AllocObject((uint)(pMT->_uBaseSize + numElements * pMT->_usComponentSize)); + *result = pMT; + *(int*)(result + 1) = numElements; + return result; + } + + internal struct ArrayElement + { + public object Value; + } + + [RuntimeExport("RhpStelemRef")] + public static unsafe void StelemRef(Array array, nint index, object obj) + { + ref object element = ref Unsafe.As(array)[index].Value; + + MethodTable* elementType = array.m_pMethodTable->_relatedType; + + if (obj == null) + goto assigningNull; + + if (elementType != obj.m_pMethodTable) + Environment.FailFast(null); /* covariance */ + +doWrite: + element = obj; + return; + +assigningNull: + element = null; + return; + } + + [RuntimeExport("RhpCheckedAssignRef")] + public static unsafe void RhpCheckedAssignRef(void** dst, void* r) + { + *dst = r; + } + + [RuntimeExport("RhpAssignRef")] + public static unsafe void RhpAssignRef(void** dst, void* r) + { + *dst = r; + } + + static unsafe MethodTable** AllocObject(uint size) + { +#if WINDOWS + [DllImport("kernel32")] + static extern MethodTable** LocalAlloc(uint flags, uint size); + MethodTable** result = LocalAlloc(0x40, size); +#else + [DllImport("libSystem.Native")] + static extern MethodTable** SystemNative_Malloc(nuint size); + MethodTable** result = SystemNative_Malloc(size); +#endif + + return result; + } + } +} diff --git a/src/zerolib/README.md b/src/zerolib/README.md new file mode 100644 index 0000000..0192575 --- /dev/null +++ b/src/zerolib/README.md @@ -0,0 +1,8 @@ +# ZeroLib minimal runtime library + +There are two guiding principles: + +1. Public API surface that doesn't exist in .NET cannot be added (i.e. source code compilable against zerolib needs to be compilable against .NET). +2. APIs that do hidden allocations cannot be added. If an API returns a fresh object instance, it's all good. If an API allocates as part of it's operation and doesn't return that as result, it's not good. We don't have a GC. This is all memory leaks. + +To work in this codebase, simply add a file with a `Main` to this directory and `bflat build --stdlib:none`. You'll get a tight inner dev loop. But you can do things differently; more power to you. diff --git a/src/zerolib/System/Array.cs b/src/zerolib/System/Array.cs new file mode 100644 index 0000000..bd24545 --- /dev/null +++ b/src/zerolib/System/Array.cs @@ -0,0 +1,27 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public abstract class Array + { + private readonly int _length; + + public int Length => _length; + } + + class Array : Array { } +} diff --git a/src/zerolib/System/Attribute.cs b/src/zerolib/System/Attribute.cs new file mode 100644 index 0000000..d0b6488 --- /dev/null +++ b/src/zerolib/System/Attribute.cs @@ -0,0 +1,42 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public abstract class Attribute { } + + public enum AttributeTargets { } + + public sealed class AttributeUsageAttribute : Attribute + { + //Constructors + public AttributeUsageAttribute(AttributeTargets validOn) + { + } + + public bool AllowMultiple + { + get { return false; } + set { } + } + + public bool Inherited + { + get { return false; } + set { } + } + } +} diff --git a/src/zerolib/System/Console.Unix.cs b/src/zerolib/System/Console.Unix.cs new file mode 100644 index 0000000..5dde41e --- /dev/null +++ b/src/zerolib/System/Console.Unix.cs @@ -0,0 +1,226 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if !WINDOWS + +using System.Runtime.InteropServices; + +namespace System +{ + public static unsafe partial class Console + { + public static unsafe string Title + { + set + { + _ = value; + } + } + + public static unsafe bool CursorVisible + { + set + { + _ = value; + } + } + + public static unsafe void SetWindowSize(int x, int y) + { + } + + public static void SetBufferSize(int x, int y) + { + } + + public static ConsoleColor ForegroundColor + { + set + { + ConsoleColor adjusted = value; + if (value >= ConsoleColor.DarkBlue && value <= ConsoleColor.Gray) + adjusted = value - ConsoleColor.DarkBlue + ConsoleColor.Blue; + int colorCode = adjusted switch + { + ConsoleColor.Black => 30, + ConsoleColor.Blue => 34, + ConsoleColor.Green => 32, + ConsoleColor.Cyan => 36, + ConsoleColor.Red => 31, + ConsoleColor.Magenta => 35, + ConsoleColor.Yellow => 33, + _ => 37, + }; + byte* pBuf = stackalloc byte[16]; + pBuf[0] = 0x1B; + pBuf[1] = (byte)'['; + byte* pCur = Append(&pBuf[2], colorCode); + *pCur++ = (byte)'m'; + SystemNative_Log(pBuf, (int)(pCur - pBuf)); + } + } + + public static void SetCursorPosition(int x, int y) + { + byte* pBuf = stackalloc byte[32]; + pBuf[0] = 0x1B; + pBuf[1] = (byte)'['; + byte* cur = Append(&pBuf[2], y); + *cur++ = (byte)';'; + cur = Append(cur, x); + *cur++ = (byte)'H'; + SystemNative_Log(pBuf, (int)(cur - pBuf)); + } + + static byte* Append(byte* b, int x) + { + if (x >= 10) + b = Append(b, x / 10); + *b = (byte)((x % 10) + '0'); + return ++b; + } + + [DllImport("libSystem.Native")] + private static extern void SystemNative_Log(void* pBuffer, int length); + + public static void Write(char c) + { + if (c <= 0x7F) + { + SystemNative_Log(&c, 1); + } + else if (c <= 0x7FF) + { + ushort twoByte; + byte* pOut = (byte*)&twoByte; + *pOut++ = (byte)(0xC0 | (c >> 6)); + *pOut++ = (byte)(0x80 | (c & 0x3F)); + SystemNative_Log(&twoByte, 2); + } + else + { + int threeByte; + byte* pOut = (byte*)&threeByte; + *pOut++ = (byte)(0xE0 | (c >> 12)); + *pOut++ = (byte)(0x80 | ((c >> 6) & 0x3F)); + *pOut++ = (byte)(0x80 | (c & 0x3F)); + SystemNative_Log(&threeByte, 3); + } + } + + public static bool KeyAvailable => StdInReader.KeyAvailable(); + + public static ConsoleKeyInfo ReadKey(bool intercept) => StdInReader.ReadKey(); + + static class StdInReader + { + static StdInReader() + { + SystemNative_InitializeTerminalAndSignalHandling(); + + [DllImport("libSystem.Native")] + static extern int SystemNative_InitializeTerminalAndSignalHandling(); + } + + private static StdInBuffer s_buffer; + + struct StdInBuffer + { + public const int Size = 256; + public int StartIndex; + public int EndIndex; + public bool Empty => StartIndex >= EndIndex; + public fixed char Chars[Size]; + } + + public static bool KeyAvailable() + { + return SystemNative_StdinReady(1) != 0; + + [DllImport("libSystem.Native")] + static extern int SystemNative_StdinReady(int distinguishNewLines); + } + + public static ConsoleKeyInfo ReadKey() + { + SystemNative_InitializeConsoleBeforeRead(0, 1, 0); + + [DllImport("libSystem.Native")] + static extern void SystemNative_InitializeConsoleBeforeRead(int distinguishNewLines, byte minChars, byte decisecondsTimeout); + + try + { + if (s_buffer.Empty) + { + byte* bufPtr = stackalloc byte[StdInBuffer.Size]; + int result = SystemNative_ReadStdin(bufPtr, StdInBuffer.Size); + + [DllImport("libSystem.Native")] + static extern unsafe int SystemNative_ReadStdin(byte* buffer, int bufferSize); + + if (result <= 0) + { + return default; + } + s_buffer.StartIndex = 0; + s_buffer.EndIndex = result; + for (int i = 0; i < result; i++) + { + char c = (char)bufPtr[i]; + if (c > 0x7F) + Environment.FailFast(null); + s_buffer.Chars[i] = c; + } + } + + if (s_buffer.EndIndex - s_buffer.StartIndex >= 3 && + s_buffer.Chars[s_buffer.StartIndex] == 0x1B && + s_buffer.Chars[s_buffer.StartIndex + 1] == '[') + { + char code = s_buffer.Chars[s_buffer.StartIndex + 2]; + s_buffer.StartIndex += 3; + switch (code) + { + case 'A': + return new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false); + case 'B': + return new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false); + case 'C': + return new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false); + case 'D': + return new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false); + default: + Environment.FailFast(null); + return default; + } + } + + return default; + + } + finally + { + SystemNative_UninitializeConsoleAfterRead(); + + [DllImport("libSystem.Native")] + static extern void SystemNative_UninitializeConsoleAfterRead(); + } + } + } + } +} + +#endif diff --git a/src/zerolib/System/Console.Windows.cs b/src/zerolib/System/Console.Windows.cs new file mode 100644 index 0000000..1912192 --- /dev/null +++ b/src/zerolib/System/Console.Windows.cs @@ -0,0 +1,195 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if WINDOWS + +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Console + { + private enum BOOL : int + { + FALSE = 0, + TRUE = 1, + } + + [DllImport("kernel32")] + private static unsafe extern IntPtr GetStdHandle(int c); + + private readonly static IntPtr s_outputHandle = GetStdHandle(-11); + + private readonly static IntPtr s_inputHandle = GetStdHandle(-10); + + [DllImport("kernel32", EntryPoint = "SetConsoleTitleW")] + private static unsafe extern BOOL SetConsoleTitle(char* c); + + public static unsafe string Title + { + set + { + fixed (char* c = value) + SetConsoleTitle(c); + } + } + + [StructLayout(LayoutKind.Sequential)] + struct CONSOLE_CURSOR_INFO + { + public uint Size; + public BOOL Visible; + } + + [DllImport("kernel32")] + private static unsafe extern BOOL SetConsoleCursorInfo(IntPtr handle, CONSOLE_CURSOR_INFO* cursorInfo); + + public static unsafe bool CursorVisible + { + set + { + CONSOLE_CURSOR_INFO cursorInfo = new CONSOLE_CURSOR_INFO + { + Size = 1, + Visible = value ? BOOL.TRUE : BOOL.FALSE + }; + SetConsoleCursorInfo(s_outputHandle, &cursorInfo); + } + } + + [DllImport("kernel32")] + private static unsafe extern BOOL SetConsoleTextAttribute(IntPtr handle, ushort attribute); + + public static ConsoleColor ForegroundColor + { + set + { + SetConsoleTextAttribute(s_outputHandle, (ushort)value); + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct KEY_EVENT_RECORD + { + public BOOL KeyDown; + public short RepeatCount; + public short VirtualKeyCode; + public short VirtualScanCode; + public short UChar; + public int ControlKeyState; + } + + [StructLayout(LayoutKind.Sequential)] + private struct INPUT_RECORD + { + public short EventType; + public KEY_EVENT_RECORD KeyEvent; + } + + [DllImport("kernel32", EntryPoint = "PeekConsoleInputW", CharSet = CharSet.Unicode)] + private static unsafe extern BOOL PeekConsoleInput(IntPtr hConsoleInput, INPUT_RECORD* lpBuffer, uint nLength, uint* lpNumberOfEventsRead); + + public static unsafe bool KeyAvailable + { + get + { + uint nRead; + INPUT_RECORD buffer; + while (true) + { + PeekConsoleInput(s_inputHandle, &buffer, 1, &nRead); + + if (nRead == 0) + return false; + + if (buffer.EventType == 1 && buffer.KeyEvent.KeyDown != BOOL.FALSE) + return true; + + ReadConsoleInput(s_inputHandle, &buffer, 1, &nRead); + } + } + } + + [DllImport("kernel32", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] + private static unsafe extern BOOL ReadConsoleInput(IntPtr hConsoleInput, INPUT_RECORD* lpBuffer, uint nLength, uint* lpNumberOfEventsRead); + + public static unsafe ConsoleKeyInfo ReadKey(bool intercept) + { + uint nRead; + INPUT_RECORD buffer; + do + { + ReadConsoleInput(s_inputHandle, &buffer, 1, &nRead); + } + while (buffer.EventType != 1 || buffer.KeyEvent.KeyDown == BOOL.FALSE); + + return new ConsoleKeyInfo((char)buffer.KeyEvent.UChar, (ConsoleKey)buffer.KeyEvent.VirtualKeyCode, false, false, false); + } + + struct SMALL_RECT + { + public short Left, Top, Right, Bottom; + } + + [DllImport("kernel32")] + private static unsafe extern BOOL SetConsoleWindowInfo(IntPtr handle, BOOL absolute, SMALL_RECT* consoleWindow); + + public static unsafe void SetWindowSize(int x, int y) + { + SMALL_RECT rect = new SMALL_RECT + { + Left = 0, + Top = 0, + Right = (short)(x - 1), + Bottom = (short)(y - 1), + }; + SetConsoleWindowInfo(s_outputHandle, BOOL.TRUE, &rect); + } + + [StructLayout(LayoutKind.Sequential)] + struct COORD + { + public short X, Y; + } + + [DllImport("kernel32")] + private static unsafe extern BOOL SetConsoleScreenBufferSize(IntPtr handle, COORD size); + + public static void SetBufferSize(int x, int y) + { + SetConsoleScreenBufferSize(s_outputHandle, new COORD { X = (short)x, Y = (short)y }); + } + + [DllImport("kernel32")] + private static unsafe extern BOOL SetConsoleCursorPosition(IntPtr handle, COORD position); + + public static void SetCursorPosition(int x, int y) + { + SetConsoleCursorPosition(s_outputHandle, new COORD { X = (short)x, Y = (short)y }); + } + + [DllImport("kernel32", EntryPoint = "WriteConsoleW")] + private static unsafe extern BOOL WriteConsole(IntPtr handle, void* buffer, int numChars, int* charsWritten, void* reserved); + + public static unsafe void Write(char c) + { + int dummy; + WriteConsole(s_outputHandle, &c, 1, &dummy, null); + } + } +} + +#endif diff --git a/src/zerolib/System/Console.cs b/src/zerolib/System/Console.cs new file mode 100644 index 0000000..7ee4052 --- /dev/null +++ b/src/zerolib/System/Console.cs @@ -0,0 +1,56 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public enum ConsoleColor + { + Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, + Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White + } + + public enum ConsoleKey + { + Escape = 27, + LeftArrow = 37, + UpArrow = 38, + RightArrow = 39, + DownArrow = 40, + } + + public readonly struct ConsoleKeyInfo + { + public ConsoleKeyInfo(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + Key = key; + } + + public readonly ConsoleKey Key; + } + + public static unsafe partial class Console + { + public static void WriteLine(string s) + { + for (int i = 0; i < s.Length; i++) + Console.Write(s[i]); +#if WINDOWS + Console.Write('\r'); +#endif + Console.Write('\n'); + } + } +} diff --git a/src/zerolib/System/Delegate.cs b/src/zerolib/System/Delegate.cs new file mode 100644 index 0000000..16c03a4 --- /dev/null +++ b/src/zerolib/System/Delegate.cs @@ -0,0 +1,49 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public abstract class Delegate + { + internal object _firstParameter; + internal object _helperObject; + internal nint _extraFunctionPointerOrData; + internal IntPtr _functionPointer; + + private void InitializeClosedStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) + { + _extraFunctionPointerOrData = functionPointer; + _helperObject = firstParameter; + _functionPointer = functionPointerThunk; + _firstParameter = this; + } + + private void InitializeOpenStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) + { + _firstParameter = this; + _functionPointer = functionPointerThunk; + _extraFunctionPointerOrData = functionPointer; + } + + private void InitializeClosedInstance(object firstParameter, IntPtr functionPointer) + { + _functionPointer = functionPointer; + _firstParameter = firstParameter; + } + } + + public abstract class MulticastDelegate : Delegate { } +} diff --git a/src/zerolib/System/Enum.cs b/src/zerolib/System/Enum.cs new file mode 100644 index 0000000..aa012fa --- /dev/null +++ b/src/zerolib/System/Enum.cs @@ -0,0 +1,20 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public abstract class Enum : ValueType { } +} diff --git a/src/zerolib/System/Environment.Unix.cs b/src/zerolib/System/Environment.Unix.cs new file mode 100644 index 0000000..656fe03 --- /dev/null +++ b/src/zerolib/System/Environment.Unix.cs @@ -0,0 +1,40 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if !WINDOWS + +using System.Runtime.InteropServices; + +namespace System +{ + static partial class Environment + { + [DllImport("libSystem.Native")] + public static extern long SystemNative_GetTimestamp(); + + public static long TickCount64 => SystemNative_GetTimestamp() / 1_000_000; + + [DllImport("libSystem.Native")] + public static extern void SystemNative_Abort(); + + public static void FailFast(string message) + { + SystemNative_Abort(); + } + } +} + +#endif diff --git a/src/zerolib/System/Environment.Windows.cs b/src/zerolib/System/Environment.Windows.cs new file mode 100644 index 0000000..dea797c --- /dev/null +++ b/src/zerolib/System/Environment.Windows.cs @@ -0,0 +1,40 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if WINDOWS + +using System.Runtime.InteropServices; + +namespace System +{ + static partial class Environment + { + [DllImport("kernel32")] + private static extern long GetTickCount64(); + + public static long TickCount64 => GetTickCount64(); + + [DllImport("kernel32")] + private static extern void RaiseFailFastException(IntPtr a, IntPtr b, int flags); + + public static void FailFast(string message) + { + RaiseFailFastException(default, default, default); + } + } +} + +#endif diff --git a/src/zerolib/System/Nullable.cs b/src/zerolib/System/Nullable.cs new file mode 100644 index 0000000..7fc8421 --- /dev/null +++ b/src/zerolib/System/Nullable.cs @@ -0,0 +1,42 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public struct Nullable where T : struct + { + private readonly bool _hasValue; + private T _value; + + public Nullable(T value) => (_hasValue, _value) = (true, value); + + public readonly bool HasValue => _hasValue; + + public readonly T Value + { + get + { + if (!_hasValue) + Environment.FailFast(null); + return _value; + } + } + + public static implicit operator Nullable(T value) => new Nullable(value); + + public static explicit operator T(Nullable value) => value.Value; + } +} diff --git a/src/zerolib/System/Object.cs b/src/zerolib/System/Object.cs new file mode 100644 index 0000000..231f7d4 --- /dev/null +++ b/src/zerolib/System/Object.cs @@ -0,0 +1,28 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime; + +namespace System +{ + public class Object + { +#pragma warning disable 169 + // The layout of object is a contract with the compiler. + internal unsafe MethodTable* m_pMethodTable; +#pragma warning restore 169 + } +} diff --git a/src/zerolib/System/Primitives.cs b/src/zerolib/System/Primitives.cs new file mode 100644 index 0000000..c2fef86 --- /dev/null +++ b/src/zerolib/System/Primitives.cs @@ -0,0 +1,37 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public struct Void { } + + // The layout of primitive types is special cased because it would be recursive. + // These really don't need any fields to work. + public struct Boolean { } + public struct Char { } + public struct SByte { } + public struct Byte { } + public struct Int16 { } + public struct UInt16 { } + public struct Int32 { } + public struct UInt32 { } + public struct Int64 { } + public struct UInt64 { } + public struct IntPtr { } + public struct UIntPtr { } + public struct Single { } + public struct Double { } +} diff --git a/src/zerolib/System/ReadOnlySpan.cs b/src/zerolib/System/ReadOnlySpan.cs new file mode 100644 index 0000000..2c4ceae --- /dev/null +++ b/src/zerolib/System/ReadOnlySpan.cs @@ -0,0 +1,58 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public readonly ref struct ReadOnlySpan + { + private readonly ref T _reference; + private readonly int _length; + + public ReadOnlySpan(T[] array) + { + if (array == null) + { + this = default; + return; + } + + _reference = ref MemoryMarshal.GetArrayDataReference(array); + _length = array.Length; + } + + public unsafe ReadOnlySpan(void* pointer, int length) + { + _reference = ref Unsafe.As(ref *(byte*)pointer); + _length = length; + } + + public ref readonly T this[int index] + { + [Intrinsic] + get + { + if ((uint)index >= (uint)_length) + Environment.FailFast(null); + return ref Unsafe.Add(ref _reference, (nint)(uint)index); + } + } + + public static implicit operator ReadOnlySpan(T[] array) => new ReadOnlySpan(array); + } +} diff --git a/src/zerolib/System/Reflection/ReflectionAttributes.cs b/src/zerolib/System/Reflection/ReflectionAttributes.cs new file mode 100644 index 0000000..2af526a --- /dev/null +++ b/src/zerolib/System/Reflection/ReflectionAttributes.cs @@ -0,0 +1,23 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System.Reflection +{ + public sealed class DefaultMemberAttribute : Attribute + { + public DefaultMemberAttribute(string memberName) { } + } +} diff --git a/src/zerolib/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/zerolib/System/Runtime/CompilerServices/ClassConstructorRunner.cs new file mode 100644 index 0000000..e4d396f --- /dev/null +++ b/src/zerolib/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -0,0 +1,49 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + // A class responsible for running static constructors. The compiler will call into this + // code to ensure static constructors run and that they only run once. + internal static class ClassConstructorRunner + { + private static IntPtr CheckStaticClassConstructionReturnNonGCStaticBase(ref StaticClassConstructionContext context, IntPtr nonGcStaticBase) + { + CheckStaticClassConstruction(ref context); + return nonGcStaticBase; + } + + private static unsafe void CheckStaticClassConstruction(ref StaticClassConstructionContext context) + { + // Not dealing with multithreading issues. + if (context.Initialized != 1) + { + context.Initialized = 1; + context.ClassConstructor(); + } + } + } + + // This data structure is a contract with the compiler. It holds the address of a static + // constructor and a flag that specifies whether the constructor already executed. + internal unsafe struct StaticClassConstructionContext + { + public delegate* ClassConstructor; + public int Initialized; + } +} diff --git a/src/zerolib/System/Runtime/CompilerServices/CompilerAttributes.cs b/src/zerolib/System/Runtime/CompilerServices/CompilerAttributes.cs new file mode 100644 index 0000000..362b5fb --- /dev/null +++ b/src/zerolib/System/Runtime/CompilerServices/CompilerAttributes.cs @@ -0,0 +1,43 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System.Runtime.CompilerServices +{ + internal sealed class IntrinsicAttribute : Attribute { } + + public enum MethodImplOptions + { + Unmanaged = 0x0004, + NoInlining = 0x0008, + ForwardRef = 0x0010, + Synchronized = 0x0020, + NoOptimization = 0x0040, + PreserveSig = 0x0080, + AggressiveInlining = 0x0100, + AggressiveOptimization = 0x0200, + InternalCall = 0x1000 + } + + public sealed class MethodImplAttribute : Attribute + { + public MethodImplAttribute(MethodImplOptions methodImplOptions) { } + } + + public sealed class IndexerNameAttribute: Attribute + { + public IndexerNameAttribute(string indexerName) { } + } +} diff --git a/src/zerolib/System/Runtime/CompilerServices/RuntimeFeature.cs b/src/zerolib/System/Runtime/CompilerServices/RuntimeFeature.cs new file mode 100644 index 0000000..23c1f9f --- /dev/null +++ b/src/zerolib/System/Runtime/CompilerServices/RuntimeFeature.cs @@ -0,0 +1,29 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System.Runtime.CompilerServices +{ + public static class RuntimeFeature + { + public const string PortablePdb = nameof(PortablePdb); + public const string DefaultImplementationsOfInterfaces = nameof(DefaultImplementationsOfInterfaces); + public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention); + public const string CovariantReturnsOfClasses = nameof(CovariantReturnsOfClasses); + public const string ByRefFields = nameof(ByRefFields); + public const string VirtualStaticsInInterfaces = nameof(VirtualStaticsInInterfaces); + public const string NumericIntPtr = nameof(NumericIntPtr); + } +} diff --git a/src/zerolib/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/zerolib/System/Runtime/CompilerServices/RuntimeHelpers.cs new file mode 100644 index 0000000..5dcbf21 --- /dev/null +++ b/src/zerolib/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -0,0 +1,33 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + public class RuntimeHelpers + { + public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int); + } + + [StructLayout(LayoutKind.Sequential)] + internal class RawArrayData + { + public uint Length; // Array._numComponents padded to IntPtr + public uint Padding; + public byte Data; + } +} diff --git a/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs b/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs new file mode 100644 index 0000000..a4a0cf7 --- /dev/null +++ b/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs @@ -0,0 +1,32 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System.Runtime.CompilerServices +{ + public static unsafe partial class Unsafe + { + // The body of this method is generated by the compiler. + // It will do what Unsafe.Add is expected to do. It's just not possible to express it in C#. + [Intrinsic] + public static extern ref T Add(ref T source, int elementOffset); + [Intrinsic] + public static extern ref T Add(ref T source, IntPtr elementOffset); + [Intrinsic] + public static extern ref TTo As(ref TFrom source); + [Intrinsic] + public static extern T As(object o) where T : class; + } +} diff --git a/src/zerolib/System/Runtime/InteropServices/InteropAttributes.cs b/src/zerolib/System/Runtime/InteropServices/InteropAttributes.cs new file mode 100644 index 0000000..0cedc31 --- /dev/null +++ b/src/zerolib/System/Runtime/InteropServices/InteropAttributes.cs @@ -0,0 +1,56 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System.Runtime.InteropServices +{ + public enum CharSet + { + None = 1, + Ansi = 2, + Unicode = 3, + Auto = 4, + } + + public sealed class DllImportAttribute : Attribute + { + public string EntryPoint; + public CharSet CharSet; + public bool ExactSpelling; + public DllImportAttribute(string dllName) { } + } + + public sealed class UnmanagedCallersOnlyAttribute : Attribute + { + public string EntryPoint; + public UnmanagedCallersOnlyAttribute() { } + } + + public enum LayoutKind + { + Sequential = 0, + Explicit = 2, + Auto = 3, + } + + public sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) { } + } + + public sealed class InAttribute : Attribute + { + } +} diff --git a/src/zerolib/System/Runtime/InteropServices/MemoryMarshal.cs b/src/zerolib/System/Runtime/InteropServices/MemoryMarshal.cs new file mode 100644 index 0000000..a948b6b --- /dev/null +++ b/src/zerolib/System/Runtime/InteropServices/MemoryMarshal.cs @@ -0,0 +1,27 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + public static class MemoryMarshal + { + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T GetArrayDataReference(T[] array) => ref Unsafe.As(ref Unsafe.As(array).Data); + } +} diff --git a/src/zerolib/System/RuntimeHandles.cs b/src/zerolib/System/RuntimeHandles.cs new file mode 100644 index 0000000..1f3a489 --- /dev/null +++ b/src/zerolib/System/RuntimeHandles.cs @@ -0,0 +1,30 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public struct RuntimeTypeHandle + { + } + + public struct RuntimeMethodHandle + { + } + + public struct RuntimeFieldHandle + { + } +} diff --git a/src/zerolib/System/String.cs b/src/zerolib/System/String.cs new file mode 100644 index 0000000..7eedac6 --- /dev/null +++ b/src/zerolib/System/String.cs @@ -0,0 +1,81 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public sealed class String + { + // The layout of the string type is a contract with the compiler. + private readonly int _length; + private char _firstChar; + + public int Length => _length; + + [IndexerName("Chars")] + public unsafe char this[int index] + { + [System.Runtime.CompilerServices.Intrinsic] + get + { + return System.Runtime.CompilerServices.Unsafe.Add(ref _firstChar, index); + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + public extern unsafe String(char* value); + + private static unsafe string Ctor(char* ptr) + { + char* cur = ptr; + while (*cur++ != 0) ; + + string result = FastNewString((int)(cur - ptr - 1)); + for (int i = 0; i < cur - ptr - 1; i++) + Unsafe.Add(ref result._firstChar, i) = ptr[i]; + return result; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + public extern unsafe String(sbyte* value); + + private static unsafe string Ctor(sbyte* ptr) + { + sbyte* cur = ptr; + while (*cur++ != 0) ; + + string result = FastNewString((int)(cur - ptr - 1)); + for (int i = 0; i < cur - ptr - 1; i++) + { + if (ptr[i] > 0x7F) + Environment.FailFast(null); + Unsafe.Add(ref result._firstChar, i) = (char)ptr[i]; + } + return result; + } + + static unsafe string FastNewString(int numChars) + { + return NewString("".m_pMethodTable, numChars); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport("*", "RhpNewArray")] + static extern string NewString(MethodTable* pMT, int numElements); + } + } +} diff --git a/src/zerolib/System/Thread.Unix.cs b/src/zerolib/System/Thread.Unix.cs new file mode 100644 index 0000000..9bcabfd --- /dev/null +++ b/src/zerolib/System/Thread.Unix.cs @@ -0,0 +1,37 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if !WINDOWS + +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public static class Thread + { + [DllImport("libSystem.Native")] + private static extern IntPtr SystemNative_LowLevelMonitor_Create(); + + [DllImport("libSystem.Native")] + private static extern void SystemNative_LowLevelMonitor_TimedWait(IntPtr mon, int ms); + + private static IntPtr s_dummyMonitor = SystemNative_LowLevelMonitor_Create(); + + public static void Sleep(int delayMs) => SystemNative_LowLevelMonitor_TimedWait(s_dummyMonitor, delayMs); + } +} + +#endif diff --git a/src/zerolib/System/Thread.Windows.cs b/src/zerolib/System/Thread.Windows.cs new file mode 100644 index 0000000..c4bcfbf --- /dev/null +++ b/src/zerolib/System/Thread.Windows.cs @@ -0,0 +1,30 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#if WINDOWS + +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public static class Thread + { + [DllImport("kernel32")] + public static extern void Sleep(int delayMs); + } +} + +#endif diff --git a/src/zerolib/System/Type.cs b/src/zerolib/System/Type.cs new file mode 100644 index 0000000..7790ae6 --- /dev/null +++ b/src/zerolib/System/Type.cs @@ -0,0 +1,21 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public class Type { } + public class RuntimeType : Type { } +} diff --git a/src/zerolib/System/ValueTuple.cs b/src/zerolib/System/ValueTuple.cs new file mode 100644 index 0000000..f6943f5 --- /dev/null +++ b/src/zerolib/System/ValueTuple.cs @@ -0,0 +1,30 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + + public ValueTuple(T1 t1, T2 t2) + { + Item1 = t1; + Item2 = t2; + } + } +} diff --git a/src/zerolib/System/ValueType.cs b/src/zerolib/System/ValueType.cs new file mode 100644 index 0000000..2770b3f --- /dev/null +++ b/src/zerolib/System/ValueType.cs @@ -0,0 +1,20 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace System +{ + public abstract class ValueType { } +}