├── .github ├── FUNDING.yml ├── official-release-license.tmp └── workflows │ └── cicd.yml ├── .gitignore ├── BUILDING.md ├── LICENSE ├── README.md ├── samples ├── DynamicLibrary │ ├── README.md │ ├── library.cs │ └── libraryconsumer.cs ├── HelloWorld │ ├── README.md │ └── hello.cs ├── MinimalSize │ ├── README.md │ └── minimalsize.cs ├── Snake │ ├── FrameBuffer.cs │ ├── Game.cs │ ├── README.md │ ├── Random.cs │ └── Snake.cs └── Sokol │ ├── README.md │ ├── saudio.cs │ └── triangle.cs └── src ├── Directory.Build.props ├── bflat.Tests ├── BflatCompilation.cs ├── BflatCompilationResult.cs ├── UnitTest1.cs └── bflat.Tests.csproj ├── bflat ├── BflatTypeSystemContext.cs ├── BuildCommand.cs ├── CommandBase.cs ├── CommonOptions.cs ├── ConfigurablePInvokePolicy.cs ├── ILBuildCommand.cs ├── InstructionSetHelpers.cs ├── PerfWatch.cs ├── Program.cs └── bflat.csproj ├── debloat ├── Program.cs └── debloat.csproj └── zerolib ├── Internal ├── Runtime │ └── CompilerHelpers │ │ └── InteropHelpers.cs ├── Startup.Efi.cs ├── Startup.Unix.cs ├── Startup.Windows.cs └── Stubs.cs ├── README.md └── System ├── Array.cs ├── Attribute.cs ├── Console.Efi.cs ├── Console.Unix.cs ├── Console.Windows.cs ├── Console.cs ├── Delegate.cs ├── Enum.cs ├── Environment.Efi.cs ├── Environment.Unix.cs ├── Environment.Windows.cs ├── Nullable.cs ├── Object.Efi.cs ├── Object.cs ├── Primitives.cs ├── ReadOnlySpan.cs ├── Reflection └── ReflectionAttributes.cs ├── Runtime ├── CompilerServices │ ├── ClassConstructorRunner.cs │ ├── CompilerAttributes.cs │ ├── RuntimeFeature.cs │ ├── RuntimeHelpers.cs │ └── Unsafe.cs └── InteropServices │ ├── InteropAttributes.cs │ └── MemoryMarshal.cs ├── RuntimeHandles.cs ├── Span.cs ├── String.cs ├── Thread.Efi.cs ├── Thread.Unix.cs ├── Thread.Windows.cs ├── Type.cs ├── ValueTuple.cs └── ValueType.cs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: [ "https://paypal.me/MichalStrehovsky" ] 2 | -------------------------------------------------------------------------------- /.github/official-release-license.tmp: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Michal Strehovsky 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: 'Release version to create' 11 | required: true 12 | 13 | jobs: 14 | build_and_test: 15 | strategy: 16 | matrix: 17 | include: 18 | - vm: ubuntu-latest 19 | # - vm: windows-latest 20 | runs-on: ${{ matrix.vm }} 21 | name: Build and test ${{ matrix.vm }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@v2 25 | - name: Setup dotnet 26 | uses: actions/setup-dotnet@v3 27 | with: 28 | dotnet-version: '8.0.x' 29 | dotnet-quality: 'preview' 30 | - name: Ask Github to please let us use the package registry 31 | run: | 32 | cd src/bflat 33 | dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/bflattened/index.json" 34 | - name: Build (CI) 35 | if: ${{ github.event.inputs.version == '' }} 36 | run: dotnet build src/bflat/bflat.csproj -t:BuildLayouts -c:Release 37 | - name: Build (CD) 38 | if: ${{ github.event.inputs.version != '' }} 39 | run: dotnet build src/bflat/bflat.csproj -t:BuildLayouts -c:Release -p:Version=${{ github.event.inputs.version }} 40 | - name: Debloat the libs 41 | run: | 42 | dotnet run --project src/debloat/debloat.csproj layouts/windows-x64/lib 43 | dotnet run --project src/debloat/debloat.csproj layouts/windows-arm64/lib 44 | dotnet run --project src/debloat/debloat.csproj layouts/linux-glibc-x64/lib 45 | dotnet run --project src/debloat/debloat.csproj layouts/linux-glibc-arm64/lib 46 | - name: Prepare symbols 47 | run: | 48 | mv layouts/windows-x64/bflat.pdb layouts/obj/windows-x64/ 49 | mv layouts/windows-arm64/bflat.pdb layouts/obj/windows-arm64/ 50 | sudo apt-get update 51 | sudo apt-get install llvm 52 | llvm-objcopy --only-keep-debug layouts/linux-glibc-x64/bflat layouts/obj/linux-glibc-x64/bflat.dwo 53 | llvm-objcopy --strip-debug --strip-unneeded layouts/linux-glibc-x64/bflat 54 | llvm-objcopy --add-gnu-debuglink=layouts/obj/linux-glibc-x64/bflat.dwo layouts/linux-glibc-x64/bflat 55 | llvm-objcopy --only-keep-debug layouts/linux-glibc-arm64/bflat layouts/obj/linux-glibc-arm64/bflat.dwo 56 | llvm-objcopy --strip-debug --strip-unneeded layouts/linux-glibc-arm64/bflat 57 | llvm-objcopy --add-gnu-debuglink=layouts/obj/linux-glibc-arm64/bflat.dwo layouts/linux-glibc-arm64/bflat 58 | - name: Inject license 59 | if: ${{ github.event.inputs.version != '' }} 60 | run: | 61 | cp .github/official-release-license.tmp layouts/windows-x64/LICENSE.TXT 62 | cp .github/official-release-license.tmp layouts/windows-arm64/LICENSE.TXT 63 | cp .github/official-release-license.tmp layouts/linux-glibc-x64/LICENSE.TXT 64 | cp .github/official-release-license.tmp layouts/linux-glibc-arm64/LICENSE.TXT 65 | - name: Create archives 66 | run: | 67 | (cd layouts/windows-x64 && zip -r ../windows-x64.zip .) 68 | (cd layouts/windows-arm64 && zip -r ../windows-arm64.zip .) 69 | zip -j layouts/windows-x64-symbols.zip layouts/obj/windows-x64/bflat.pdb 70 | zip -j layouts/windows-arm64-symbols.zip layouts/obj/windows-arm64/bflat.pdb 71 | tar -C layouts/linux-glibc-x64/ -czvf layouts/linux-glibc-x64.tar.gz . 72 | tar -C layouts/obj/linux-glibc-x64/ -czvf layouts/linux-glibc-x64-symbols.tar.gz bflat.dwo 73 | tar -C layouts/linux-glibc-arm64/ -czvf layouts/linux-glibc-arm64.tar.gz . 74 | tar -C layouts/obj/linux-glibc-arm64/ -czvf layouts/linux-glibc-arm64-symbols.tar.gz bflat.dwo 75 | - name: Archive windows-x64 hosted compiler 76 | uses: actions/upload-artifact@v2 77 | with: 78 | name: bflat-windows-x64.zip 79 | path: layouts/windows-x64.zip 80 | - name: Archive windows-x64 hosted compiler symbols 81 | uses: actions/upload-artifact@v2 82 | with: 83 | name: debugsymbols-bflat-windows-x64.zip 84 | path: layouts/windows-x64-symbols.zip 85 | - name: Archive windows-arm64 hosted compiler 86 | uses: actions/upload-artifact@v2 87 | with: 88 | name: bflat-windows-arm64.zip 89 | path: layouts/windows-arm64.zip 90 | - name: Archive windows-arm64 hosted compiler symbols 91 | uses: actions/upload-artifact@v2 92 | with: 93 | name: debugsymbols-bflat-windows-arm64.zip 94 | path: layouts/windows-arm64-symbols.zip 95 | - name: Archive linux-glibc-x64 hosted compiler 96 | uses: actions/upload-artifact@v2 97 | with: 98 | name: bflat-linux-glibc-x64.zip 99 | path: layouts/linux-glibc-x64.tar.gz 100 | - name: Archive linux-glibc-x64 hosted compiler symbols 101 | uses: actions/upload-artifact@v2 102 | with: 103 | name: debugsymbols-bflat-linux-glibc-x64.zip 104 | path: layouts/linux-glibc-x64-symbols.tar.gz 105 | - name: Archive linux-glibc-arm64 hosted compiler 106 | uses: actions/upload-artifact@v2 107 | with: 108 | name: bflat-linux-glibc-arm64.zip 109 | path: layouts/linux-glibc-arm64.tar.gz 110 | - name: Archive linux-glibc-arm64 hosted compiler symbols 111 | uses: actions/upload-artifact@v2 112 | with: 113 | name: debugsymbols-bflat-linux-glibc-arm64.zip 114 | path: layouts/linux-glibc-arm64-symbols.tar.gz 115 | 116 | - name: Run tests 117 | if: ${{ github.event.inputs.version == '' }} 118 | run: dotnet test src/bflat.Tests/bflat.Tests.csproj 119 | 120 | - name: Create tag 121 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 122 | run: | 123 | git tag v${{ github.event.inputs.version }} 124 | git push origin v${{ github.event.inputs.version }} 125 | - name: Create Release 126 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 127 | id: create_release 128 | uses: actions/create-release@v1 129 | env: 130 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 131 | with: 132 | tag_name: v${{ github.event.inputs.version }} 133 | release_name: v${{ github.event.inputs.version }} 134 | draft: true 135 | prerelease: false 136 | 137 | - name: Upload windows-x64 hosted compiler 138 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 139 | uses: actions/upload-release-asset@v1 140 | env: 141 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 142 | with: 143 | upload_url: ${{ steps.create_release.outputs.upload_url }} 144 | asset_path: layouts/windows-x64.zip 145 | asset_name: bflat-${{ github.event.inputs.version }}-windows-x64.zip 146 | asset_content_type: application/zip 147 | - name: Upload windows-x64 hosted compiler symbols 148 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky'}} 149 | uses: actions/upload-release-asset@v1 150 | env: 151 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 152 | with: 153 | upload_url: ${{ steps.create_release.outputs.upload_url }} 154 | asset_path: layouts/windows-x64-symbols.zip 155 | asset_name: bflat-${{ github.event.inputs.version }}-windows-x64-debugsymbols.zip 156 | asset_content_type: application/zip 157 | 158 | - name: Upload windows-arm64 hosted compiler 159 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 160 | uses: actions/upload-release-asset@v1 161 | env: 162 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 163 | with: 164 | upload_url: ${{ steps.create_release.outputs.upload_url }} 165 | asset_path: layouts/windows-arm64.zip 166 | asset_name: bflat-${{ github.event.inputs.version }}-windows-arm64.zip 167 | asset_content_type: application/zip 168 | - name: Upload windows-arm64 hosted compiler symbols 169 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky'}} 170 | uses: actions/upload-release-asset@v1 171 | env: 172 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 173 | with: 174 | upload_url: ${{ steps.create_release.outputs.upload_url }} 175 | asset_path: layouts/windows-arm64-symbols.zip 176 | asset_name: bflat-${{ github.event.inputs.version }}-windows-arm64-debugsymbols.zip 177 | asset_content_type: application/zip 178 | 179 | - name: Upload linux-glibc-x64 hosted compiler 180 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 181 | uses: actions/upload-release-asset@v1 182 | env: 183 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 184 | with: 185 | upload_url: ${{ steps.create_release.outputs.upload_url }} 186 | asset_path: layouts/linux-glibc-x64.tar.gz 187 | asset_name: bflat-${{ github.event.inputs.version }}-linux-glibc-x64.tar.gz 188 | asset_content_type: application/gzip 189 | - name: Upload linux-glibc-x64 hosted compiler symbols 190 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 191 | uses: actions/upload-release-asset@v1 192 | env: 193 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 194 | with: 195 | upload_url: ${{ steps.create_release.outputs.upload_url }} 196 | asset_path: layouts/linux-glibc-x64-symbols.tar.gz 197 | asset_name: bflat-${{ github.event.inputs.version }}-linux-glibc-x64-debugsymbols.tar.gz 198 | asset_content_type: application/gzip 199 | 200 | - name: Upload linux-glibc-arm64 hosted compiler 201 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 202 | uses: actions/upload-release-asset@v1 203 | env: 204 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 205 | with: 206 | upload_url: ${{ steps.create_release.outputs.upload_url }} 207 | asset_path: layouts/linux-glibc-arm64.tar.gz 208 | asset_name: bflat-${{ github.event.inputs.version }}-linux-glibc-arm64.tar.gz 209 | asset_content_type: application/gzip 210 | - name: Upload linux-glibc-arm64 hosted compiler symbols 211 | if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }} 212 | uses: actions/upload-release-asset@v1 213 | env: 214 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 215 | with: 216 | upload_url: ${{ steps.create_release.outputs.upload_url }} 217 | asset_path: layouts/linux-glibc-arm64-symbols.tar.gz 218 | asset_name: bflat-${{ github.event.inputs.version }}-linux-glibc-arm64-debugsymbols.tar.gz 219 | asset_content_type: application/gzip 220 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | layouts 4 | [Bb]in/ 5 | [Oo]bj/ 6 | src/bflat/[Nn]u[Gg]et.config 7 | launchSettings.json 8 | .vs/ 9 | msbuild.binlog 10 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building bflat from source 2 | 3 | You'll need the .NET SDK to build bflat. The shipping binaries of bflat are built with bflat, but the .NET SDK is used for bootstrapping. 4 | 5 | Before you can build bflat, you need to make sure you can restore the packages built out of the bflattened/runtime repo. For reasons that escape me, NuGet packages published to the Github registry require authentication. You need a github account and you need to create a PAT token to read packages. Follow the information [here](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry). 6 | 7 | You should end up with a nuget.config file in src/bflat/ that looks roughly like this: 8 | 9 | ```xml 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ``` 23 | 24 | In retrospect, going with Github packages was a mistake, but I don't have the capacity to redo things that work right now. NuGet.config is in .gitignore so that you don't accidentally check it in. But to be doubly sure, make sure your PAT can only read packages and nothing else. Leaking such PAT would likely cause no damage to most people. 25 | 26 | With the package issue out of the way, you can run bflat by executing: 27 | 28 | ```bash 29 | $ dotnet run --project src/bflat/bflat.csproj 30 | ``` 31 | 32 | from the repo root, or build binaries by running: 33 | 34 | ```bash 35 | $ dotnet build src/bflat/bflat.csproj 36 | ``` 37 | 38 | This will build/run bflat on top of the official .NET runtime. 39 | 40 | To create bflat-compiled versions of bflat, run: 41 | 42 | ```bash 43 | $ dotnet build src/bflat/bflat.csproj -t:BuildLayouts 44 | ``` 45 | 46 | This will create a `layouts` directory at the repo root and place Linux- and Windows-hosted versions of the bflat compiler built with bflat. These are the bits that are available as prebuilt binaries. 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bflat 2 | 3 | ### https://flattened.net 4 | 5 | C# as you know it but with Go-inspired tooling that produces small, selfcontained, and native executables out of the box. 6 | 7 | ```console 8 | $ echo 'System.Console.WriteLine("Hello World");' > hello.cs 9 | $ bflat build hello.cs 10 | $ ./hello 11 | Hello World 12 | $ bflat build hello.cs --os:windows 13 | $ file ./hello.exe 14 | hello.exe: PE32+ executable (console) x86-64, for MS Windows 15 | ``` 16 | 17 | ## 🎻 What exactly is bflat 18 | 19 | bflat is a concoction of Roslyn - the "official" C# compiler that produces .NET executables - and NativeAOT (née CoreRT) - the ahead of time compiler for .NET based on CoreCLR. Thanks to this, you get access to the latest C# features using the high performance CoreCLR GC and native code generator (RyuJIT). 20 | 21 | bflat merges the two components together into a single ahead of time crosscompiler and runtime for C#. 22 | 23 | bflat can currently target: 24 | 25 | * x64/arm64 glibc-based Linux (2.17 or later on x64 (~CentOS 7), or 2.27 or later on arm64 (~Ubuntu 18.04)) 26 | * arm64 bionic-based Linux (Android API level 21) 27 | * x64/arm64 Windows (Windows 7 or later) 28 | * x64/arm64 UEFI (only with `--stdlib:zero`) 29 | 30 | Support for musl-based Linux is in the works. 31 | 32 | bflat can either produce native executables, or native shared libraries that can be called from other languages through FFI. 33 | 34 | ## 🥁 Where to get bflat 35 | 36 | Look at the [Releases tab](https://github.com/bflattened/bflat/releases) of this repo and download a compiler that matches your host system. These are all crosscompilers and can target any of the supported OSes/architectures. 37 | 38 | Unzip the archive to a convenient location and add the root to your PATH. You're all set. See the samples directory for a couple samples. 39 | 40 | On Windows, you can also grab it from winget: `winget install bflat`. 41 | 42 | The binary releases are licensed under the MIT license. 43 | 44 | ## 🎷 I don't see dotnet, MSBuild, or NuGet 45 | 46 | That's the point. bflat is to dotnet as VS Code is to VS. 47 | 48 | ## 🎙 Where is the source code 49 | 50 | The source code is split between this repo and [bflattened/runtime](https://github.com/bflattened/runtime). The bflattened/runtime repo is a regularly updated fork of the [dotnet/runtime](https://github.com/dotnet/runtime) repo that contains non-upstreamable bflat-specific changes. The bflattened/runtime repo produces compiler and runtime binaries that this repo consumes. 51 | 52 | ## 📚 Two standard libraries 53 | 54 | bflat comes with two standard libraries. The first one (called `DotNet`) is the default and comes from the dotnet/runtime repo fork. It includes everything you know and love from .NET. The second one (called `Zero`) is a minimal standard library that doesn't have much more than just primitive types. The source code for it lives in this repo. Switch between those with `--stdlib:zero` argument. 55 | 56 | ## 📻 How to stay up-to-date on bflat? 57 | 58 | Follow me on [Bluesky](https://bsky.app/profile/migeel.sk). 59 | 60 | ## 🎺 Optimizing output for size 61 | 62 | By default, bflat produces executables that are between 1 MB and 2 MB in size, even for the simplest apps. There are multiple reasons for this: 63 | 64 | * bflat includes stack trace data about all compiled methods so that it can print pretty exception stack traces 65 | * even the simplest apps might end up calling into reflection (to e.g. get the name of the `OutOfMemoryException` class), globalization, etc. 66 | * method bodies are aligned at 16-byte boundaries to optimize CPU cache line utilization 67 | * (Doesn't apply to Windows) DWARF debug information is included in the executable 68 | 69 | The "bigger" defaults are chosen for friendliness and convenience. To get an experience that more closely matches low level programming languages, specify `--no-reflection`, `--no-stacktrace-data`, `--no-globalization`, and `--no-exception-messages` arguments to `bflat build`. 70 | 71 | Best to show an example. Following program: 72 | 73 | ```csharp 74 | using System.Diagnostics; 75 | using static System.Console; 76 | 77 | WriteLine($"NullReferenceException message is: {new NullReferenceException().Message}"); 78 | WriteLine($"The runtime type of int is named: {typeof(int)}"); 79 | WriteLine($"Type of boxed integer is{(123.GetType() == typeof(int) ? "" : " not")} equal to typeof(int)"); 80 | WriteLine($"Type of boxed integer is{(123.GetType() == typeof(byte) ? "" : " not")} equal to typeof(byte)"); 81 | WriteLine($"Upper case of 'Вторник' is '{"Вторник".ToUpper()}'"); 82 | WriteLine($"Current stack frame is {new StackTrace().GetFrame(0)}"); 83 | ``` 84 | 85 | will print this by default: 86 | 87 | ``` 88 | NullReferenceException message is: Object reference not set to an instance of an object. 89 | The runtime type of int is named: System.Int32 90 | Type of boxed integer is equal to typeof(int) 91 | Type of boxed integer is not equal to typeof(byte) 92 | Upper case of 'Вторник' is 'ВТОРНИК' 93 | Current stack frame is $.
$(String[]) + 0x154 at offset 340 in file:line:column :0:0 94 | ``` 95 | 96 | But it will print this with all above arguments specified: 97 | 98 | ``` 99 | NullReferenceException message is: Arg_NullReferenceException 100 | The runtime type of int is named: EETypeRva:0x00048BD0 101 | Type of boxed integer is equal to typeof(int) 102 | Type of boxed integer is not equal to typeof(byte) 103 | Upper case of 'Вторник' is 'Вторник' 104 | Current stack frame is ms!+0xb82d4 at offset 340 in file:line:column :0:0 105 | ``` 106 | 107 | With all options turned on, one can comfortably fit useful programs under 1 MB. The above program is 708 kB on Windows at the time of writing this. The output executables are executables like any other. You can add `-Os --no-pie --separate-symbols` for even more savings and use a tool like UPX to compress them further (to ~300 kB range). 108 | 109 | If you're targeting a Unix-like system, you might want to pass `--separate-symbols` to place debug information into a separate file (debug information is big!). This is not needed on Windows because the platform convention is to place debug information in a separate PDB file already. 110 | 111 | ## 🎸 Preprocessor definitions 112 | 113 | Besides the preprocessor definitions provided at the command line, bflat defines several other symbols: `BFLAT` (defined always), `DEBUG` (defined when not optimizing), `WINDOWS`/`LINUX`/`UEFI` (when the corresponding operating system is the target), `X64`/`ARM64` (when the corresponding architecture is targeted). 114 | 115 | ## 🎹 Debugging bflat apps 116 | 117 | Apps compiled with bflat debug same as any other native code. Launch the produced executable under your favorite debugger (gdb or lldb on Linux, or Visual Studio or WinDbg on Windows) and you'll be able to set breakpoints, step, and see local variables. 118 | 119 | ## ☝ Samples 120 | 121 | The repo has samples with README in the `samples` directory. Clone the repo and try the samples yourself! 122 | -------------------------------------------------------------------------------- /samples/DynamicLibrary/README.md: -------------------------------------------------------------------------------- 1 | # Shared library sample 2 | 3 | To build a shared library run following command: 4 | 5 | ```console 6 | $ bflat build library.cs 7 | ``` 8 | 9 | Bflat automatically detects you're trying to build a library because there's no Main. You can also specify `--target` to bflat. 10 | 11 | This will produce a library.so file on Linux and library.dll on Windows. 12 | 13 | The library can be consumed from any other programming language. Since we're using C#, let's consume it from C#. Because at this point library.so/.dll is a native library like any other, we need to p/invoke into it. 14 | 15 | ```console 16 | $ bflat build libraryconsumer.cs 17 | ``` 18 | 19 | This will build a libraryconsumer(.exe) binary that will load the library and invoke functions in it. 20 | 21 | Libraryconsumer is going to load the library through dlopen/dlsym (LoadLibrary/GetProcAddress on Windows). If you would like, you can also have it bind statically. Build the consumer program like this: 22 | 23 | ```console 24 | $ bflat build libraryconsumer.cs -i:library 25 | ``` 26 | 27 | This will build the program with a hard reference to the external symbol. You should see a failure during linking if you run the above command. To fix this failure, we need to pass information where to find the symbol to linker. 28 | 29 | If you're targeting Windows, run: 30 | 31 | ```console 32 | $ bflat build libraryconsumer.cs -i:library --ldflags:library.lib 33 | ``` 34 | 35 | This will point the linker to the import library generated when building the library. 36 | -------------------------------------------------------------------------------- /samples/DynamicLibrary/library.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | internal static class Library 5 | { 6 | [UnmanagedCallersOnly(EntryPoint = "Add")] 7 | private static int Add(int num1, int num2) 8 | { 9 | return num1 + num2; 10 | } 11 | 12 | // Note that parameters to UnmanagedCallersOnly methods may only be primitive types (except bool), 13 | // pointers and structures consisting of the above. Reference types are not permitted because they don't 14 | // have an ABI representation on the native side. 15 | [UnmanagedCallersOnly(EntryPoint = "CountCharacters")] 16 | private static unsafe nint CountCharacters(byte* pChars) 17 | { 18 | byte* pCurrent = pChars; 19 | while (*pCurrent++ != 0) ; 20 | return (nint)(pCurrent - pChars) - 1; 21 | } 22 | 23 | // Note that it is not allowed to leak exceptions out of UnmanagedCallersOnly methods. 24 | // There's no ABI-defined way to propagate the exception across native code. 25 | [UnmanagedCallersOnly(EntryPoint = "TryDivide")] 26 | private static unsafe int TryDivide(int num1, int num2, int* result) 27 | { 28 | try 29 | { 30 | *result = num1 / num2; 31 | } 32 | catch (DivideByZeroException ex) 33 | { 34 | return 0; 35 | } 36 | return 1; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/DynamicLibrary/libraryconsumer.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using static System.Console; 3 | 4 | WriteLine($"Add(1, 2) = {Add(1, 2)}"); 5 | WriteLine($"CountCharacters(\"Hello\") = {CountCharacters("Hello")}"); 6 | WriteLine($"TryDivide(1, 0, out _) = {TryDivide(1, 0, out _)}"); 7 | 8 | [DllImport("library")] 9 | static extern int Add(int num1, int num2); 10 | 11 | [DllImport("library", CharSet = CharSet.Ansi)] 12 | static extern nint CountCharacters(string s); 13 | 14 | [DllImport("library")] 15 | static extern bool TryDivide(int num1, int num2, out int result); 16 | -------------------------------------------------------------------------------- /samples/HelloWorld/README.md: -------------------------------------------------------------------------------- 1 | # Hello world sample 2 | 3 | This is a basic hello world with a twist that if you run it with an argument, it will print Hello $Argument. 4 | 5 | To build: 6 | 7 | ```console 8 | $ bflat build hello.cs 9 | ``` 10 | 11 | This will produce a hello(.exe) file that is native compiled. 12 | -------------------------------------------------------------------------------- /samples/HelloWorld/hello.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | if (args.Length == 0) 4 | { 5 | Console.WriteLine("Hello world!"); 6 | } 7 | else 8 | { 9 | Console.WriteLine($"Hello {args[0]}!"); 10 | } 11 | -------------------------------------------------------------------------------- /samples/MinimalSize/README.md: -------------------------------------------------------------------------------- 1 | # Minimal size sample 2 | 3 | This demonstrates how to build a minimal size executable with bflat. 4 | 5 | ```console 6 | $ bflat build minimalsize.cs --no-reflection --no-stacktrace-data --no-globalization --no-exception-messages -Os --no-pie --separate-symbols 7 | ``` 8 | 9 | This will produce a minimalsize(.exe) file that is native compiled. You can launch it. Observe the difference in runtime behavior and size of the output when you omit some of the arguments from the `bflat build` command line above. 10 | -------------------------------------------------------------------------------- /samples/MinimalSize/minimalsize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | Console.WriteLine($"NullReferenceException message is: {new NullReferenceException().Message}"); 5 | Console.WriteLine($"The runtime type of int is named: {typeof(int)}"); 6 | Console.WriteLine($"Type of boxed integer is{(123.GetType() == typeof(int) ? "" : " not")} equal to typeof(int)"); 7 | Console.WriteLine($"Type of boxed integer is{(123.GetType() == typeof(byte) ? "" : " not")} equal to typeof(byte)"); 8 | Console.WriteLine($"Upper case of 'Вторник' is '{"Вторник".ToUpper()}'"); 9 | Console.WriteLine($"Current stack frame is {new StackTrace().GetFrame(0)}"); 10 | -------------------------------------------------------------------------------- /samples/Snake/FrameBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | unsafe struct FrameBuffer 4 | { 5 | public const int Width = 40; 6 | public const int Height = 20; 7 | public const int Area = Width * Height; 8 | 9 | fixed char _chars[Area]; 10 | 11 | public void SetPixel(int x, int y, char character) 12 | { 13 | _chars[y * Width + x] = character; 14 | } 15 | 16 | public void Clear() 17 | { 18 | for (int i = 0; i < Area; i++) 19 | _chars[i] = ' '; 20 | } 21 | 22 | public readonly void Render() 23 | { 24 | const ConsoleColor snakeColor = ConsoleColor.Green; 25 | 26 | Console.ForegroundColor = snakeColor; 27 | 28 | for (int i = 0; i < Area; i++) 29 | { 30 | if (i % Width == 0) 31 | Console.SetCursorPosition(0, i / Width); 32 | 33 | char c = _chars[i]; 34 | 35 | if (c == '*' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) 36 | { 37 | Console.ForegroundColor = c == '*' ? ConsoleColor.Red : ConsoleColor.White; 38 | Console.Write(c); 39 | Console.ForegroundColor = snakeColor; 40 | } 41 | else 42 | Console.Write(c); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /samples/Snake/Game.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Thread = System.Threading.Thread; 4 | 5 | struct Game 6 | { 7 | enum Result 8 | { 9 | Win, Loss 10 | } 11 | 12 | private Random _random; 13 | 14 | private Game(uint randomSeed) 15 | { 16 | _random = new Random(randomSeed); 17 | } 18 | 19 | private Result Run(ref FrameBuffer fb) 20 | { 21 | Snake s = new Snake( 22 | (byte)(_random.Next() % FrameBuffer.Width), 23 | (byte)(_random.Next() % FrameBuffer.Height), 24 | (Snake.Direction)(_random.Next() % 4)); 25 | 26 | MakeFood(s, out byte foodX, out byte foodY); 27 | 28 | while (true) 29 | { 30 | fb.Clear(); 31 | 32 | if (!s.Update()) 33 | { 34 | s.Draw(ref fb); 35 | return Result.Loss; 36 | } 37 | 38 | s.Draw(ref fb); 39 | 40 | if (Console.KeyAvailable) 41 | { 42 | ConsoleKeyInfo ki = Console.ReadKey(intercept: true); 43 | switch (ki.Key) 44 | { 45 | case ConsoleKey.UpArrow: 46 | s.Course = Snake.Direction.Up; break; 47 | case ConsoleKey.DownArrow: 48 | s.Course = Snake.Direction.Down; break; 49 | case ConsoleKey.LeftArrow: 50 | s.Course = Snake.Direction.Left; break; 51 | case ConsoleKey.RightArrow: 52 | s.Course = Snake.Direction.Right; break; 53 | } 54 | } 55 | 56 | if (s.HitTest(foodX, foodY)) 57 | { 58 | if (s.Extend()) 59 | MakeFood(s, out foodX, out foodY); 60 | else 61 | return Result.Win; 62 | } 63 | 64 | fb.SetPixel(foodX, foodY, '*'); 65 | 66 | fb.Render(); 67 | 68 | Thread.Sleep(100); 69 | } 70 | } 71 | 72 | void MakeFood(in Snake snake, out byte foodX, out byte foodY) 73 | { 74 | do 75 | { 76 | foodX = (byte)(_random.Next() % FrameBuffer.Width); 77 | foodY = (byte)(_random.Next() % FrameBuffer.Height); 78 | } 79 | while (snake.HitTest(foodX, foodY)); 80 | } 81 | 82 | public static void Main() 83 | { 84 | #if WINDOWS 85 | Console.SetWindowSize(FrameBuffer.Width, FrameBuffer.Height + 1); 86 | Console.SetBufferSize(FrameBuffer.Width, FrameBuffer.Height + 1); 87 | Console.Title = "See Sharp Snake"; 88 | Console.CursorVisible = false; 89 | #endif 90 | 91 | FrameBuffer fb = new FrameBuffer(); 92 | 93 | while (true) 94 | { 95 | #if UEFI 96 | // Work around TickCount crashing on QEMU 97 | Game g = new Game(0); 98 | #else 99 | Game g = new Game((uint)Environment.TickCount64); 100 | #endif 101 | Result result = g.Run(ref fb); 102 | 103 | string message = result == Result.Win ? "You win" : "You lose"; 104 | 105 | int position = (FrameBuffer.Width - message.Length) / 2; 106 | for (int i = 0; i < message.Length; i++) 107 | { 108 | fb.SetPixel(position + i, FrameBuffer.Height / 2, message[i]); 109 | } 110 | 111 | fb.Render(); 112 | 113 | Console.ReadKey(intercept: true); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /samples/Snake/README.md: -------------------------------------------------------------------------------- 1 | # ZeroLib sample 2 | 3 | Basides the .NET standard library, the bflat compiler also comes with a minimal standard library that is a very minimal subset of what's in .NET. 4 | 5 | There is no garbage collector. No exception handling. No useful collection types. Just a couple things to run at least _some_ code. 6 | 7 | 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 else. 8 | 9 | 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. 10 | 11 | To build the sample with zerolib, run: 12 | 13 | ```console 14 | $ bflat build --stdlib:zero 15 | ``` 16 | 17 | 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`. 18 | 19 | You should see a fully selfcontained executable that is 10-50 kB in size, depending on the platform. That's the "art" part. 20 | 21 | ## Building UEFI boot applications 22 | 23 | ZeroLib also supports the UEFI environment so you can build apps that run on bare metal with it. 24 | 25 | To build the snake game as a UEFI boot application, run: 26 | 27 | ```console 28 | $ mkdir -p efi/boot 29 | $ bflat build --stdlib:zero --os:uefi -o:efi/boot/bootx64.efi 30 | ``` 31 | 32 | This will produce a `bootx64.efi` file under `efi/boot` that can be used as a boot loader. 33 | 34 | To run this with QEMU, execute: 35 | 36 | ```console 37 | qemu-system-x86_64 -bios /usr/OVMF/OVMF_CODE.fd -hdd fat:rw:. 38 | ``` 39 | 40 | The first parameter points the system to an EFI firmware (BIOS is the default). The second parameter tells QEMU to emulate a FAT filesystem starting at the current directory (this assumes there is a `./efi/boot/bootx64.efi` file). You may need to `sudo apt install ovmf` to get the EFI firmware for QEMU. 41 | 42 | If you're on Windows, you can also execute this under Hyper-V. 43 | 44 | First create a virtual disk with a FAT32 filesystem: 45 | 46 | ```console 47 | $ diskpart 48 | create vdisk file=disk.vhdx maximum=500 49 | select vdisk file=disk.vhdx 50 | attach vdisk 51 | convert gpt 52 | create partition efi size=100 53 | format quick fs=fat32 label="System" 54 | assign letter="X" 55 | exit 56 | $ xcopy efi\boot\BOOTX64.EFI X:\EFI\BOOT\ 57 | $ diskpart 58 | select vdisk file=disk.vhdx 59 | select partition 2 60 | remove letter=X 61 | detach vdisk 62 | exit 63 | ``` 64 | 65 | Then create a Gen2 virtual machine with the hard disk. Make sure to disable secure boot. 66 | 67 | -------------------------------------------------------------------------------- /samples/Snake/Random.cs: -------------------------------------------------------------------------------- 1 | struct Random 2 | { 3 | private uint _val; 4 | 5 | public Random(uint seed) 6 | { 7 | _val = seed; 8 | } 9 | 10 | public uint Next() => _val = (1103515245 * _val + 12345) % 2147483648; 11 | } -------------------------------------------------------------------------------- /samples/Snake/Snake.cs: -------------------------------------------------------------------------------- 1 | struct Snake 2 | { 3 | public const int MaxLength = 30; 4 | 5 | private int _length; 6 | 7 | // Body is a packed integer that packs the X coordinate, Y coordinate, and the character 8 | // for the snake's body. 9 | // Only primitive types can be used with C# `fixed`, hence this is an `int`. 10 | private unsafe fixed int _body[MaxLength]; 11 | 12 | private Direction _direction; 13 | private Direction _oldDirection; 14 | 15 | public Direction Course 16 | { 17 | set 18 | { 19 | if (_oldDirection != _direction) 20 | _oldDirection = _direction; 21 | 22 | if (_direction - value != 2 && value - _direction != 2) 23 | _direction = value; 24 | } 25 | } 26 | 27 | public unsafe Snake(byte x, byte y, Direction direction) 28 | { 29 | _body[0] = new Part(x, y, DirectionToChar(direction, direction)).Pack(); 30 | _direction = direction; 31 | _oldDirection = direction; 32 | _length = 1; 33 | } 34 | 35 | public unsafe bool Update() 36 | { 37 | Part oldHead = Part.Unpack(_body[0]); 38 | Part newHead = new Part( 39 | (byte)(_direction switch 40 | { 41 | Direction.Left => oldHead.X == 0 ? FrameBuffer.Width - 1 : oldHead.X - 1, 42 | Direction.Right => (oldHead.X + 1) % FrameBuffer.Width, 43 | _ => oldHead.X, 44 | }), 45 | (byte)(_direction switch 46 | { 47 | Direction.Up => oldHead.Y == 0 ? FrameBuffer.Height - 1 : oldHead.Y - 1, 48 | Direction.Down => (oldHead.Y + 1) % FrameBuffer.Height, 49 | _ => oldHead.Y, 50 | }), 51 | DirectionToChar(_direction, _direction) 52 | ); 53 | 54 | oldHead = new Part(oldHead.X, oldHead.Y, DirectionToChar(_oldDirection, _direction)); 55 | 56 | bool result = true; 57 | 58 | for (int i = 0; i < _length - 1; i++) 59 | { 60 | Part current = Part.Unpack(_body[i]); 61 | if (current.X == newHead.X && current.Y == newHead.Y) 62 | result = false; 63 | } 64 | 65 | _body[0] = oldHead.Pack(); 66 | 67 | for (int i = _length - 2; i >= 0; i--) 68 | { 69 | _body[i + 1] = _body[i]; 70 | } 71 | 72 | _body[0] = newHead.Pack(); 73 | 74 | _oldDirection = _direction; 75 | 76 | return result; 77 | } 78 | 79 | public unsafe readonly void Draw(ref FrameBuffer fb) 80 | { 81 | for (int i = 0; i < _length; i++) 82 | { 83 | Part p = Part.Unpack(_body[i]); 84 | fb.SetPixel(p.X, p.Y, p.Character); 85 | } 86 | } 87 | 88 | public bool Extend() 89 | { 90 | if (_length < MaxLength) 91 | { 92 | _length += 1; 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | public unsafe readonly bool HitTest(int x, int y) 99 | { 100 | for (int i = 0; i < _length; i++) 101 | { 102 | Part current = Part.Unpack(_body[i]); 103 | if (current.X == x && current.Y == y) 104 | return true; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | private static char DirectionToChar(Direction oldDirection, Direction newDirection) 111 | { 112 | const string DirectionChangeToChar = "│┌?┐┘─┐??└│┘└?┌─"; 113 | return DirectionChangeToChar[(int)oldDirection * 4 + (int)newDirection]; 114 | } 115 | 116 | // Helper struct to pack and unpack the packed integer in _body. 117 | readonly struct Part 118 | { 119 | public readonly byte X, Y; 120 | public readonly char Character; 121 | 122 | public Part(byte x, byte y, char c) 123 | { 124 | X = x; 125 | Y = y; 126 | Character = c; 127 | } 128 | 129 | public int Pack() => X << 24 | Y << 16 | Character; 130 | public static Part Unpack(int packed) => new Part((byte)(packed >> 24), (byte)(packed >> 16), (char)packed); 131 | } 132 | 133 | public enum Direction 134 | { 135 | Up, Right, Down, Left 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /samples/Sokol/README.md: -------------------------------------------------------------------------------- 1 | # Sokol graphics/audio samples 2 | 3 | Bflat includes the [Sokol](https://github.com/floooh/sokol) crossplatform 3D and audio APIs. 4 | 5 | The C# bindings in use are these: https://github.com/MichalStrehovsky/sokol-csharp. The bindings are also available from NuGet so that they can also be used with official .NET tools outside bflat. The NuGet package is here: https://www.nuget.org/packages/sokol_csharp.unofficial/. 6 | 7 | To build the triangle sample with bflat: 8 | 9 | ```console 10 | $ bflat build triangle.cs --target:winexe 11 | ``` 12 | 13 | You can also build it as: 14 | 15 | ```console 16 | $ bflat build triangle.cs --target:winexe --no-reflection --no-stacktrace-data --no-globalization --no-exception-messages -Os --no-pie --separate-symbols 17 | ``` 18 | 19 | On Windows, this will generate a small, ~720 kB executable. 20 | 21 | To build the audio sample: 22 | 23 | ```console 24 | $ bflat build saudio.cs --target:winexe 25 | ``` 26 | 27 | More samples are avilable at https://github.com/MichalStrehovsky/sokol-csharp. They're all C# translations of https://github.com/floooh/sokol-samples. 28 | -------------------------------------------------------------------------------- /samples/Sokol/saudio.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | using Sokol; 5 | 6 | App.Run(new() { 7 | InitCb = &Init, 8 | FrameCb = &Frame, 9 | CleanupCb = &Cleanup, 10 | Width = 640, 11 | Height = 480, 12 | WindowTitle = "Saudio (sokol-app)", 13 | Icon = { SokolDefault = true }, 14 | }); 15 | 16 | [UnmanagedCallersOnly] 17 | static void Init() 18 | { 19 | Gfx.Setup(new() 20 | { 21 | Context = App.Context(), 22 | }); 23 | 24 | Audio.Setup(default); 25 | 26 | State.PassAction.Colors[0] = new() 27 | { 28 | Action = Gfx.Action.Clear, 29 | Value = new() { R = 1, G = 0.5f, B = 0, A = 1 }, 30 | }; 31 | } 32 | 33 | [UnmanagedCallersOnly] 34 | static void Frame() 35 | { 36 | Gfx.BeginDefaultPass(State.PassAction, App.Width(), App.Height()); 37 | var numFrames = Audio.Expect(); 38 | float s; 39 | for (int i = 0; i < numFrames; i++) 40 | { 41 | if ((State.EvenOdd++ & (1 << 5)) != 0) 42 | { 43 | s = 0.05f; 44 | } 45 | else 46 | { 47 | s = -0.05f; 48 | } 49 | State.Samples[State.SamplePos++] = s; 50 | if (State.SamplePos == State.Samples.Length) 51 | { 52 | State.SamplePos = 0; 53 | Audio.Push(State.Samples[0], State.Samples.Length); 54 | } 55 | 56 | } 57 | Gfx.EndPass(); 58 | Gfx.Commit(); 59 | } 60 | 61 | [UnmanagedCallersOnly] 62 | static void Cleanup() 63 | { 64 | Audio.Shutdown(); 65 | Gfx.Shutdown(); 66 | } 67 | 68 | static class State 69 | { 70 | public static Gfx.PassAction PassAction; 71 | public static int EvenOdd; 72 | public static readonly float[] Samples = new float[32]; 73 | public static int SamplePos; 74 | } 75 | -------------------------------------------------------------------------------- /samples/Sokol/triangle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | using Sokol; 5 | 6 | App.Run(new() { 7 | InitCb = &Init, 8 | FrameCb = &Frame, 9 | CleanupCb = &Cleanup, 10 | Width = 640, 11 | Height = 480, 12 | GlForceGles2 = true, 13 | WindowTitle = "Triangle (sokol-app)", 14 | Icon = { SokolDefault = true }, 15 | }); 16 | 17 | [UnmanagedCallersOnly] 18 | static void Init() 19 | { 20 | Gfx.Setup(new() 21 | { 22 | Context = App.Context(), 23 | }); 24 | 25 | /* a vertex buffer with 3 vertices */ 26 | ReadOnlySpan vertices = stackalloc float[] { 27 | // positions // colors 28 | 0.0f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 29 | 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 30 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f 31 | }; 32 | 33 | State.Bindings.VertexBuffers[0] = Gfx.MakeBuffer( 34 | vertices, 35 | "triangle-vertices"); 36 | 37 | Gfx.Shader shd = Gfx.MakeShader(GetShaderDesc()); 38 | Gfx.PipelineDesc pipelineDesc = new() 39 | { 40 | Shader = shd, 41 | Label = "triangle-pipeline", 42 | }; 43 | pipelineDesc.Layout.Attrs[0].Format = Gfx.VertexFormat.Float3; 44 | pipelineDesc.Layout.Attrs[1].Format = Gfx.VertexFormat.Float4; 45 | State.Pipeline = Gfx.MakePipeline(pipelineDesc); 46 | } 47 | 48 | [UnmanagedCallersOnly] 49 | static void Frame() 50 | { 51 | Gfx.BeginDefaultPass(default, App.Width(), App.Height()); 52 | Gfx.ApplyPipeline(State.Pipeline); 53 | Gfx.ApplyBindings(State.Bindings); 54 | Gfx.Draw(0, 3, 1); 55 | Gfx.EndPass(); 56 | Gfx.Commit(); 57 | } 58 | 59 | [UnmanagedCallersOnly] 60 | static void Cleanup() 61 | { 62 | Gfx.Shutdown(); 63 | } 64 | 65 | // build a backend-specific ShaderDesc struct 66 | // NOTE: the other samples are using shader-cross-compilation via the 67 | // sokol-shdc tool, but this sample uses a manual shader setup to 68 | // demonstrate how it works without a shader-cross-compilation tool 69 | // 70 | static Gfx.ShaderDesc GetShaderDesc() 71 | { 72 | Gfx.ShaderDesc desc = default; 73 | switch (Gfx.QueryBackend()) 74 | { 75 | case Gfx.Backend.D3d11: 76 | desc.Attrs[0].SemName = "POS"; 77 | desc.Attrs[1].SemName = "COLOR"; 78 | desc.Vs.Source = @" 79 | struct vs_in { 80 | float4 pos: POS; 81 | float4 color: COLOR; 82 | }; 83 | struct vs_out { 84 | float4 color: COLOR0; 85 | float4 pos: SV_Position; 86 | }; 87 | vs_out main(vs_in inp) { 88 | vs_out outp; 89 | outp.pos = inp.pos; 90 | outp.color = inp.color; 91 | return outp; 92 | }"; 93 | desc.Fs.Source = @" 94 | float4 main(float4 color: COLOR0): SV_Target0 { 95 | return color; 96 | }"; 97 | break; 98 | case Gfx.Backend.Glcore33: 99 | desc.Attrs[0].Name = "position"; 100 | desc.Attrs[1].Name = "color0"; 101 | desc.Vs.Source = @" 102 | #version 330 103 | in vec4 position; 104 | in vec4 color0; 105 | out vec4 color; 106 | void main() { 107 | gl_Position = position; 108 | color = color0; 109 | }"; 110 | 111 | desc.Fs.Source = @" 112 | #version 330 113 | in vec4 color; 114 | out vec4 frag_color; 115 | void main() { 116 | frag_color = color; 117 | }"; 118 | break; 119 | case Gfx.Backend.MetalMacos: 120 | desc.Vs.Source = @" 121 | #include 122 | using namespace metal; 123 | struct vs_in { 124 | float4 position [[attribute(0)]]; 125 | float4 color [[attribute(1)]]; 126 | }; 127 | struct vs_out { 128 | float4 position [[position]]; 129 | float4 color; 130 | }; 131 | vertex vs_out _main(vs_in inp [[stage_in]]) { 132 | vs_out outp; 133 | outp.position = inp.position; 134 | outp.color = inp.color; 135 | return outp; 136 | }"; 137 | desc.Fs.Source = @" 138 | #include 139 | using namespace metal; 140 | fragment float4 _main(float4 color [[stage_in]]) { 141 | return color; 142 | };"; 143 | break; 144 | } 145 | return desc; 146 | } 147 | 148 | static class State 149 | { 150 | public static Gfx.Pipeline Pipeline; 151 | public static Gfx.Bindings Bindings; 152 | } 153 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(MSBuildThisFileDirectory)..\layouts\ 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/bflat.Tests/BflatCompilation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace bflat.Tests 9 | { 10 | internal class BflatCompilation 11 | { 12 | private string _exeExtension; 13 | private readonly string _compilerPath; 14 | 15 | public BflatCompilation() 16 | : this(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "windows" : "linux-glibc", 17 | RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()) 18 | { } 19 | 20 | public BflatCompilation(string osPart, string archPart) 21 | { 22 | _compilerPath = FindCompiler(osPart, archPart); 23 | _exeExtension = osPart == "windows" ? ".exe" : ""; 24 | } 25 | 26 | public BflatCompilationResult Build(string source, string arguments = null) 27 | { 28 | string fileWithoutExtension = Path.GetTempFileName(); 29 | File.WriteAllText(fileWithoutExtension + ".cs", source); 30 | 31 | StringBuilder argBuilder = new StringBuilder("build ") 32 | .Append(arguments) 33 | .Append(" \"") 34 | .Append(fileWithoutExtension) 35 | .Append(".cs\" ") 36 | .Append("-o:\"") 37 | .Append(fileWithoutExtension) 38 | .Append(_exeExtension) 39 | .Append("\" "); 40 | 41 | var psi = new ProcessStartInfo(_compilerPath, argBuilder.ToString()) 42 | { 43 | RedirectStandardError = true, 44 | RedirectStandardOutput = true, 45 | }; 46 | 47 | var p = Process.Start(psi); 48 | if (!p.WaitForExit(30000)) 49 | { 50 | p.Kill(entireProcessTree: true); 51 | throw new Exception("Timed out"); 52 | } 53 | 54 | string stdOut = p.StandardOutput.ReadToEnd(); 55 | string stdErr = p.StandardError.ReadToEnd(); 56 | 57 | if (p.ExitCode != 0) 58 | { 59 | throw new Exception($"Non-zero exit code\nStdout:\n{stdOut}\nStderr:\n{stdErr}"); 60 | } 61 | 62 | return new BflatCompilationResult(fileWithoutExtension + _exeExtension, stdErr, stdOut); 63 | } 64 | 65 | private static string FindCompiler(string os, string arch) 66 | { 67 | string compilerTuple = $"{os}-{arch}"; 68 | 69 | string compilerName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bflat.exe" : "bflat"; 70 | 71 | string startPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 72 | string currentPath = startPath; 73 | string root = Path.GetPathRoot(currentPath); 74 | 75 | while (currentPath != root) 76 | { 77 | string candidate = Path.Combine(currentPath, "layouts", compilerTuple, compilerName); 78 | if (File.Exists(candidate)) 79 | { 80 | return candidate; 81 | } 82 | currentPath = Path.GetDirectoryName(currentPath); 83 | } 84 | 85 | throw new Exception($"Compiler '{compilerName}' for '{compilerTuple}' not found in '{startPath}' (reached '{currentPath}'). Did you build the layouts before running tests?"); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/bflat.Tests/BflatCompilationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Xunit; 4 | 5 | namespace bflat.Tests 6 | { 7 | internal record BflatCompilationResult(string BinaryName, string StdErr, string StdOut) 8 | { 9 | public void Run(string expectedOutput = null) 10 | { 11 | var psi = new ProcessStartInfo(BinaryName) 12 | { 13 | RedirectStandardError = true, 14 | RedirectStandardOutput = true, 15 | }; 16 | 17 | var p = Process.Start(psi); 18 | if (!p.WaitForExit(30000)) 19 | { 20 | p.Kill(entireProcessTree: true); 21 | throw new Exception("Timed out"); 22 | } 23 | 24 | string stdOut = p.StandardOutput.ReadToEnd(); 25 | 26 | if (expectedOutput != null) 27 | { 28 | Assert.Equal(expectedOutput, stdOut); 29 | } 30 | 31 | Assert.Equal(0, p.ExitCode); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/bflat.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace bflat.Tests; 5 | 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | new BflatCompilation().Build("System.Console.WriteLine(\"Hello\");").Run("Hello" + Environment.NewLine); 12 | } 13 | } -------------------------------------------------------------------------------- /src/bflat.Tests/bflat.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | all 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/bflat/BflatTypeSystemContext.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.IO; 18 | using System.IO.MemoryMappedFiles; 19 | using System.Reflection.PortableExecutable; 20 | 21 | using Internal.IL; 22 | using Internal.TypeSystem; 23 | using Internal.TypeSystem.Ecma; 24 | 25 | using ILCompiler; 26 | 27 | class BflatTypeSystemContext : CompilerTypeSystemContext 28 | { 29 | public unsafe BflatTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode, DelegateFeature delegateFeatures, MemoryStream compiledModule, string compiledModuleName) 30 | : base(details, genericsMode, delegateFeatures) 31 | { 32 | var mappedFile = MemoryMappedFile.CreateNew(mapName: null, compiledModule.Length); 33 | var vs = mappedFile.CreateViewStream(); 34 | compiledModule.CopyTo(vs); 35 | compiledModule.Dispose(); 36 | vs.Dispose(); 37 | 38 | var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); 39 | 40 | var safeBuffer = accessor.SafeMemoryMappedViewHandle; 41 | var peReader = new PEReader((byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength); 42 | 43 | var pdbReader = PortablePdbSymbolReader.TryOpenEmbedded(peReader, GetMetadataStringDecoder()); 44 | 45 | CacheOpenModule(compiledModuleName, compiledModuleName, EcmaModule.Create(this, peReader, null, pdbReader), accessor); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/bflat/CommandBase.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.CommandLine.Invocation; 18 | using System.CommandLine.Parsing; 19 | using System.Threading.Tasks; 20 | 21 | internal abstract class CommandBase : ICommandHandler 22 | { 23 | public Task InvokeAsync(InvocationContext context) => Task.FromResult(Handle(context.ParseResult)); 24 | public int Invoke(InvocationContext context) => Handle(context.ParseResult); 25 | public abstract int Handle(ParseResult result); 26 | } 27 | -------------------------------------------------------------------------------- /src/bflat/CommonOptions.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using Microsoft.CodeAnalysis; 18 | using System; 19 | using System.Collections.Generic; 20 | using System.CommandLine; 21 | using System.IO; 22 | using System.Linq; 23 | 24 | internal static class CommonOptions 25 | { 26 | public static Option ReferencesOption = 27 | new Option(new string[] { "-r", "--reference" }, 28 | "Additional .NET assemblies to include") 29 | { 30 | ArgumentHelpName = "file list" 31 | }; 32 | 33 | public static Option VerbosityOption = 34 | new Option("--verbose", 35 | "Enable verbose logging"); 36 | 37 | public static Option StdLibOption = 38 | new Option("--stdlib", 39 | "C# standard library to use"); 40 | 41 | public static Option DeterministicOption = 42 | new Option("--deterministic", 43 | "Produce deterministic outputs including timestamps"); 44 | 45 | public static Option OutputOption = 46 | new Option(new string[] { "-o", "--out" }, 47 | "Output file path") 48 | { 49 | ArgumentHelpName = "file" 50 | }; 51 | 52 | public static Option DefinedSymbolsOption = 53 | new Option(new string[] { "-d", "--define" }, 54 | "Define conditional compilation symbol(s)"); 55 | 56 | public static Option ResourceOption = 57 | new Option(new string[] { "-res", "--resource" }, 58 | "Managed resource to include") 59 | { 60 | ArgumentHelpName = "[,[,public|private]]", 61 | }; 62 | 63 | 64 | public static Option TargetOption = 65 | new Option("--target", 66 | "Build target"); 67 | 68 | public static Argument InputFilesArgument = new Argument() { HelpName = "file list" }; 69 | 70 | public static Option NoDebugInfoOption = new Option("--no-debug-info", "Disable generation of debug information"); 71 | 72 | public static Option LangVersionOption = 73 | new Option("--langversion", 74 | "C# language version ('latest', 'default', 'latestmajor', 'preview', or version like '6' or '7.1'"); 75 | 76 | public static string GetOutputFileNameWithoutSuffix(string[] inputFileNames) 77 | { 78 | string outputFileName; 79 | if (inputFileNames.Length == 0) 80 | outputFileName = Path.GetFileName(Directory.GetCurrentDirectory()); 81 | else 82 | outputFileName = Path.GetFileNameWithoutExtension(inputFileNames[0]); 83 | 84 | outputFileName = Path.Combine(Directory.GetCurrentDirectory(), outputFileName); 85 | 86 | return outputFileName; 87 | } 88 | 89 | public static string HomePath { get; } = Environment.GetEnvironmentVariable("BFLAT_HOME") ?? AppContext.BaseDirectory; 90 | 91 | public static string[] GetInputFiles(string[] inputFileNames) 92 | { 93 | if (inputFileNames.Length > 0) 94 | return inputFileNames; 95 | 96 | return Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.cs", SearchOption.AllDirectories).ToArray(); 97 | } 98 | 99 | public static string[] GetReferencePaths(string[] referencePaths, StandardLibType stdlib) 100 | { 101 | if (stdlib == StandardLibType.None) 102 | return referencePaths; 103 | 104 | List result = new List(referencePaths); 105 | string refPath = Path.Combine(HomePath, "ref"); 106 | if (stdlib == StandardLibType.Zero) 107 | { 108 | result.Add(Path.Combine(refPath, "zerolib.dll")); 109 | } 110 | else 111 | { 112 | foreach (var f in Directory.GetFiles(refPath, "*.dll")) 113 | { 114 | if (f.EndsWith("zerolib.dll")) 115 | continue; 116 | result.Add(f); 117 | } 118 | } 119 | return result.ToArray(); 120 | } 121 | 122 | public static IEnumerable GetResourceDescriptions(string[] resinfos) 123 | { 124 | foreach (var resinfo in resinfos) 125 | { 126 | var components = resinfo.Split(','); 127 | string fileName = components[0]; 128 | string name = Path.GetFileName(fileName); 129 | if (components.Length > 1) 130 | { 131 | name = components[1]; 132 | } 133 | bool pub = true; 134 | if (components.Length > 2) 135 | { 136 | pub = components[2] != "private"; 137 | } 138 | 139 | yield return new ResourceDescription(name, () => File.OpenRead(fileName), pub); 140 | } 141 | } 142 | } 143 | 144 | public enum BuildTargetType 145 | { 146 | Exe = 1, 147 | WinExe, 148 | Shared, 149 | } 150 | 151 | public enum StandardLibType 152 | { 153 | DotNet, 154 | None, 155 | Zero, 156 | } 157 | 158 | public enum TargetOS 159 | { 160 | Unknown, 161 | Windows, 162 | Linux, 163 | UEFI, 164 | } 165 | -------------------------------------------------------------------------------- /src/bflat/ConfigurablePInvokePolicy.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Runtime.InteropServices; 8 | 9 | using Internal.IL; 10 | using Internal.TypeSystem; 11 | 12 | namespace ILCompiler 13 | { 14 | class ConfigurablePInvokePolicy : PInvokeILEmitterConfiguration 15 | { 16 | private readonly TargetDetails _target; 17 | private readonly Dictionary> _directPInvokes; 18 | 19 | public ConfigurablePInvokePolicy(TargetDetails target, IReadOnlyList directPInvokes, IReadOnlyList directPInvokeLists) 20 | { 21 | _directPInvokes = new Dictionary>(target.IsWindows ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); 22 | 23 | // * is always a direct call 24 | _directPInvokes.Add("*", null); 25 | 26 | foreach (var file in directPInvokeLists) 27 | { 28 | foreach (var entry in File.ReadLines(file)) 29 | { 30 | AddDirectPInvoke(entry); 31 | } 32 | } 33 | 34 | foreach (var entry in directPInvokes) 35 | { 36 | AddDirectPInvoke(entry); 37 | } 38 | 39 | _target = target; 40 | } 41 | 42 | private void AddDirectPInvoke(string entry) 43 | { 44 | // Ignore comments 45 | if (entry.StartsWith('#')) 46 | return; 47 | 48 | entry = entry.Trim(); 49 | 50 | // Ignore empty entries 51 | if (string.IsNullOrEmpty(entry)) 52 | return; 53 | 54 | int separator = entry.IndexOf('!'); 55 | 56 | if (separator != -1) 57 | { 58 | string libraryName = entry.Substring(0, separator); 59 | string entrypointName = entry.Substring(separator + 1); 60 | 61 | if (_directPInvokes.TryGetValue(libraryName, out HashSet entrypointSet)) 62 | { 63 | // All entrypoints from the library are direct 64 | if (entrypointSet == null) 65 | return; 66 | } 67 | else 68 | { 69 | _directPInvokes.Add(libraryName, entrypointSet = new HashSet()); 70 | } 71 | 72 | entrypointSet.Add(entrypointName); 73 | } 74 | else 75 | { 76 | // All entrypoints from the library are direct 77 | _directPInvokes[entry] = null; 78 | } 79 | } 80 | 81 | private IEnumerable ModuleNameVariations(string name) 82 | { 83 | yield return name; 84 | 85 | if (_target.IsWindows) 86 | { 87 | string suffix = ".dll"; 88 | 89 | if (name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) 90 | yield return name.Substring(0, name.Length - suffix.Length); 91 | } 92 | else 93 | { 94 | string suffix = _target.IsOSXLike ? ".dylib" : ".so"; 95 | 96 | if (name.EndsWith(suffix, StringComparison.Ordinal)) 97 | yield return name.Substring(0, name.Length - suffix.Length); 98 | } 99 | } 100 | 101 | private IEnumerable EntryPointNameVariations(string name, PInvokeFlags flags) 102 | { 103 | if (_target.IsWindows && !flags.ExactSpelling) 104 | { 105 | // Mirror CharSet normalization from Marshaller.CreateMarshaller 106 | bool isAnsi = flags.CharSet switch 107 | { 108 | CharSet.Ansi => true, 109 | CharSet.Unicode => false, 110 | CharSet.Auto => false, 111 | _ => true 112 | }; 113 | 114 | if (isAnsi) 115 | { 116 | // For ANSI, look for the user-provided entry point name first. 117 | // If that does not exist, try the charset suffix. 118 | yield return name; 119 | yield return name + "A"; 120 | } 121 | else 122 | { 123 | // For Unicode, look for the entry point name with the charset suffix first. 124 | // The 'W' API takes precedence over the undecorated one. 125 | yield return name + "W"; 126 | yield return name; 127 | } 128 | } 129 | else 130 | { 131 | yield return name; 132 | } 133 | } 134 | 135 | public override bool GenerateDirectCall(MethodDesc method, out string externName) 136 | { 137 | var pInvokeMetadata = method.GetPInvokeMethodMetadata(); 138 | 139 | foreach (var moduleName in ModuleNameVariations(pInvokeMetadata.Module)) 140 | { 141 | if (_directPInvokes.TryGetValue(moduleName, out HashSet entrypoints)) 142 | { 143 | string entryPointMetadataName = pInvokeMetadata.Name ?? method.Name; 144 | 145 | if (entrypoints == null) 146 | { 147 | externName = entryPointMetadataName; 148 | return true; 149 | } 150 | 151 | foreach (var entryPointName in EntryPointNameVariations(entryPointMetadataName, pInvokeMetadata.Flags)) 152 | { 153 | if (entrypoints.Contains(entryPointName)) 154 | { 155 | externName = entryPointName; 156 | return true; 157 | } 158 | } 159 | } 160 | } 161 | 162 | externName = null; 163 | return false; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/bflat/ILBuildCommand.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #pragma warning disable 8509 18 | 19 | using System; 20 | using System.Collections.Generic; 21 | using System.CommandLine; 22 | using System.CommandLine.Parsing; 23 | using System.IO; 24 | using System.Linq; 25 | using System.Threading; 26 | 27 | using Microsoft.CodeAnalysis; 28 | using Microsoft.CodeAnalysis.CSharp; 29 | using Microsoft.CodeAnalysis.Emit; 30 | using Microsoft.CodeAnalysis.Text; 31 | 32 | using Internal.TypeSystem; 33 | 34 | internal class ILBuildCommand : CommandBase 35 | { 36 | private ILBuildCommand() { } 37 | 38 | private static Option OptimizeOption = new Option("-O", "Enable optimizations"); 39 | 40 | public static Command Create() 41 | { 42 | var command = new Command("build-il", "Compiles the specified C# source files into IL format") 43 | { 44 | CommonOptions.InputFilesArgument, 45 | CommonOptions.DefinedSymbolsOption, 46 | CommonOptions.ReferencesOption, 47 | CommonOptions.StdLibOption, 48 | CommonOptions.DeterministicOption, 49 | CommonOptions.VerbosityOption, 50 | CommonOptions.OutputOption, 51 | CommonOptions.TargetOption, 52 | CommonOptions.ResourceOption, 53 | CommonOptions.NoDebugInfoOption, 54 | CommonOptions.LangVersionOption, 55 | OptimizeOption, 56 | }; 57 | command.Handler = new ILBuildCommand(); 58 | 59 | return command; 60 | } 61 | 62 | public override int Handle(ParseResult result) 63 | { 64 | string[] userSpecifiedInputFiles = result.GetValueForArgument(CommonOptions.InputFilesArgument); 65 | string[] inputFiles = CommonOptions.GetInputFiles(userSpecifiedInputFiles); 66 | string[] defines = result.GetValueForOption(CommonOptions.DefinedSymbolsOption); 67 | string[] references = CommonOptions.GetReferencePaths( 68 | result.GetValueForOption(CommonOptions.ReferencesOption), 69 | result.GetValueForOption(CommonOptions.StdLibOption)); 70 | 71 | OptimizationLevel optimizationLevel = result.GetValueForOption(OptimizeOption) ? OptimizationLevel.Release : OptimizationLevel.Debug; 72 | 73 | string userSpecificedOutputFileName = result.GetValueForOption(CommonOptions.OutputOption); 74 | string outputNameWithoutSuffix = 75 | userSpecificedOutputFileName != null ? Path.GetFileNameWithoutExtension(userSpecificedOutputFileName) : 76 | CommonOptions.GetOutputFileNameWithoutSuffix(userSpecifiedInputFiles); 77 | 78 | BuildTargetType buildTargetType = result.GetValueForOption(CommonOptions.TargetOption); 79 | bool deterministic = result.GetValueForOption(CommonOptions.DeterministicOption); 80 | CSharpCompilation compilation = CreateCompilation(Path.GetFileName(outputNameWithoutSuffix), inputFiles, references, defines, 81 | optimizationLevel, 82 | buildTargetType, 83 | deterministic, 84 | result.GetValueForOption(CommonOptions.LangVersionOption)); 85 | 86 | DebugInformationFormat debugInfoFormat = result.GetValueForOption(CommonOptions.NoDebugInfoOption) 87 | ? 0 : DebugInformationFormat.Embedded; 88 | var emitOptions = new EmitOptions(debugInformationFormat: debugInfoFormat); 89 | string outputFileName = userSpecificedOutputFileName; 90 | if (outputFileName == null) 91 | { 92 | bool isLibrary = buildTargetType == 0 ? 93 | compilation.GetEntryPoint(CancellationToken.None) == null : 94 | buildTargetType == BuildTargetType.Shared; 95 | string suffix = isLibrary ? ".dll" : ".exe"; 96 | outputFileName = outputNameWithoutSuffix + suffix; 97 | } 98 | 99 | var resinfos = CommonOptions.GetResourceDescriptions(result.GetValueForOption(CommonOptions.ResourceOption)); 100 | 101 | EmitResult compResult; 102 | using (var fs = File.Create(outputFileName)) 103 | { 104 | compResult = compilation.Emit(fs, manifestResources: resinfos, options: emitOptions); 105 | } 106 | 107 | if (!compResult.Success) 108 | { 109 | IEnumerable failures = compResult.Diagnostics.Where(diagnostic => 110 | diagnostic.IsWarningAsError || 111 | diagnostic.Severity == DiagnosticSeverity.Error); 112 | 113 | foreach (Diagnostic diagnostic in failures) 114 | { 115 | Console.Error.WriteLine(diagnostic.ToString()); 116 | } 117 | 118 | try 119 | { 120 | if (File.Exists(outputFileName)) 121 | File.Delete(outputFileName); 122 | } 123 | catch { } 124 | 125 | return 1; 126 | } 127 | 128 | return 0; 129 | } 130 | 131 | public static CSharpCompilation CreateCompilation( 132 | string moduleName, 133 | string[] inputFiles, 134 | string[] references, 135 | string[] userDefines, 136 | OptimizationLevel optimizationLevel, 137 | BuildTargetType buildTargetType, 138 | TargetArchitecture arch, 139 | TargetOS os, 140 | string languageVersion) 141 | { 142 | List defines = new List(userDefines) 143 | { 144 | arch switch 145 | { 146 | TargetArchitecture.X86 => "X86", 147 | TargetArchitecture.X64 => "X64", 148 | TargetArchitecture.ARM => "ARM", 149 | TargetArchitecture.ARM64 => "ARM64", 150 | }, 151 | os switch 152 | { 153 | TargetOS.Windows => "WINDOWS", 154 | TargetOS.Linux => "LINUX", 155 | TargetOS.UEFI => "UEFI", 156 | } 157 | }; 158 | 159 | return CreateCompilation(moduleName, inputFiles, references, defines.ToArray(), 160 | optimizationLevel, 161 | buildTargetType, 162 | deterministic: true, 163 | languageVersion); 164 | } 165 | 166 | private static CSharpCompilation CreateCompilation( 167 | string moduleName, 168 | string[] inputFiles, 169 | string[] references, 170 | string[] userDefines, 171 | OptimizationLevel optimizationLevel, 172 | BuildTargetType buildTargetType, 173 | bool deterministic, 174 | string languageVersion) 175 | { 176 | OutputKind? outputKind = buildTargetType switch 177 | { 178 | BuildTargetType.Shared => OutputKind.DynamicallyLinkedLibrary, 179 | BuildTargetType.WinExe => OutputKind.WindowsApplication, 180 | BuildTargetType.Exe => OutputKind.ConsoleApplication, 181 | _ => null, 182 | }; 183 | 184 | List defines = new List(userDefines) 185 | { 186 | "BFLAT" 187 | }; 188 | 189 | var metadataReferences = new List(); 190 | foreach (var reference in references) 191 | metadataReferences.Add(MetadataReference.CreateFromFile(reference)); 192 | 193 | if (!LanguageVersionFacts.TryParse(languageVersion, out LanguageVersion langVer)) 194 | { 195 | throw new Exception($"Language version '{languageVersion}' not recognized"); 196 | } 197 | 198 | var trees = new List(); 199 | foreach (var sourceFile in inputFiles) 200 | { 201 | var st = SourceText.From(File.OpenRead(sourceFile)); 202 | CSharpParseOptions parseOptions = new CSharpParseOptions( 203 | languageVersion: langVer, 204 | documentationMode: DocumentationMode.None, 205 | preprocessorSymbols: defines); 206 | string path = sourceFile; 207 | if (!Path.IsPathRooted(sourceFile)) 208 | path = Path.GetFullPath(sourceFile, Directory.GetCurrentDirectory()); 209 | trees.Add(CSharpSyntaxTree.ParseText(st, parseOptions, path)); 210 | } 211 | 212 | if (!outputKind.HasValue) 213 | { 214 | foreach (var tree in trees) 215 | { 216 | foreach (var descendant in tree.GetRoot().DescendantNodes()) 217 | { 218 | if (descendant is Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax methodSyntax) 219 | { 220 | if (methodSyntax.Identifier.Text == "Main" && 221 | methodSyntax.DescendantTokens().Any(x => x.IsKind(SyntaxKind.StaticKeyword))) 222 | { 223 | int paramCount = methodSyntax.ParameterList.Parameters.Count; 224 | if (paramCount == 0 || paramCount == 1) 225 | { 226 | outputKind = OutputKind.ConsoleApplication; 227 | } 228 | } 229 | } 230 | else if (descendant is Microsoft.CodeAnalysis.CSharp.Syntax.GlobalStatementSyntax) 231 | { 232 | outputKind = OutputKind.ConsoleApplication; 233 | } 234 | } 235 | } 236 | } 237 | 238 | if (!outputKind.HasValue) 239 | outputKind = OutputKind.DynamicallyLinkedLibrary; 240 | 241 | var compilationOptions = new CSharpCompilationOptions( 242 | outputKind.Value, 243 | allowUnsafe: true, 244 | optimizationLevel: optimizationLevel, 245 | deterministic: deterministic); 246 | return CSharpCompilation.Create(moduleName, trees, metadataReferences, compilationOptions); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/bflat/InstructionSetHelpers.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Runtime.InteropServices; 7 | 8 | using ILCompiler; 9 | 10 | using Internal.TypeSystem; 11 | 12 | using InstructionSet = Internal.JitInterface.InstructionSet; 13 | using CommandLineException = System.Exception; 14 | 15 | namespace System.CommandLine 16 | { 17 | internal static partial class Helpers 18 | { 19 | public static TargetArchitecture GetTargetArchitecture(string token) 20 | { 21 | if (string.IsNullOrEmpty(token)) 22 | { 23 | return RuntimeInformation.ProcessArchitecture switch 24 | { 25 | Architecture.X86 => TargetArchitecture.X86, 26 | Architecture.X64 => TargetArchitecture.X64, 27 | Architecture.Arm => TargetArchitecture.ARM, 28 | Architecture.Arm64 => TargetArchitecture.ARM64, 29 | Architecture.LoongArch64 => TargetArchitecture.LoongArch64, 30 | _ => throw new NotImplementedException() 31 | }; 32 | } 33 | else 34 | { 35 | return token.ToLowerInvariant() switch 36 | { 37 | "x86" => TargetArchitecture.X86, 38 | "x64" => TargetArchitecture.X64, 39 | "arm" or "armel" => TargetArchitecture.ARM, 40 | "arm64" => TargetArchitecture.ARM64, 41 | "loongarch64" => TargetArchitecture.LoongArch64, 42 | _ => throw new CommandLineException($"Target architecture '{token}' is not supported") 43 | }; 44 | } 45 | } 46 | 47 | public static InstructionSetSupport ConfigureInstructionSetSupport(string instructionSet, int maxVectorTBitWidth, bool isVectorTOptimistic, TargetArchitecture targetArchitecture, Internal.TypeSystem.TargetOS targetOS, 48 | string mustNotBeMessage, string invalidImplicationMessage, Logger logger, bool optimizingForSize = false) 49 | { 50 | InstructionSetSupportBuilder instructionSetSupportBuilder = new(targetArchitecture); 51 | 52 | // Ready to run images are built with certain instruction set baselines 53 | if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) 54 | { 55 | instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication 56 | } 57 | else if (targetArchitecture == TargetArchitecture.ARM64) 58 | { 59 | if (targetOS == Internal.TypeSystem.TargetOS.OSX) 60 | { 61 | // For osx-arm64 we know that apple-m1 is a baseline 62 | instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); 63 | } 64 | else 65 | { 66 | instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication 67 | } 68 | } 69 | 70 | // Whether to allow optimistically expanding the instruction sets beyond what was specified. 71 | // We seed this from optimizingForSize - if we're size-optimizing, we don't want to unnecessarily 72 | // compile both branches of IsSupported checks. 73 | bool allowOptimistic = !optimizingForSize; 74 | 75 | if (instructionSet == "native") 76 | { 77 | // We're compiling for a specific chip 78 | allowOptimistic = false; 79 | 80 | if (GetTargetArchitecture(null) != targetArchitecture) 81 | { 82 | throw new CommandLineException("Instruction set 'native' not supported when cross-compiling to a different architecture."); 83 | } 84 | 85 | string jitInterfaceLibrary = "jitinterface_" + RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); 86 | nint libHandle = NativeLibrary.Load(jitInterfaceLibrary, System.Reflection.Assembly.GetExecutingAssembly(), DllImportSearchPath.ApplicationDirectory); 87 | int cpuFeatures; 88 | unsafe 89 | { 90 | var getCpuFeatures = (delegate* unmanaged)NativeLibrary.GetExport(libHandle, "JitGetProcessorFeatures"); 91 | cpuFeatures = getCpuFeatures(); 92 | } 93 | HardwareIntrinsicHelpers.AddRuntimeRequiredIsaFlagsToBuilder(instructionSetSupportBuilder, cpuFeatures); 94 | 95 | if (logger.IsVerbose) 96 | logger.LogMessage($"The 'native' instruction set expanded to {instructionSetSupportBuilder}"); 97 | } 98 | else if (instructionSet != null) 99 | { 100 | List instructionSetParams = new List(); 101 | 102 | // Normalize instruction set format to include implied +. 103 | string[] instructionSetParamsInput = instructionSet.Split(','); 104 | for (int i = 0; i < instructionSetParamsInput.Length; i++) 105 | { 106 | instructionSet = instructionSetParamsInput[i]; 107 | 108 | if (string.IsNullOrEmpty(instructionSet)) 109 | throw new CommandLineException(string.Format(mustNotBeMessage, "")); 110 | 111 | char firstChar = instructionSet[0]; 112 | if ((firstChar != '+') && (firstChar != '-')) 113 | { 114 | instructionSet = "+" + instructionSet; 115 | } 116 | instructionSetParams.Add(instructionSet); 117 | } 118 | 119 | Dictionary instructionSetSpecification = new Dictionary(); 120 | foreach (string instructionSetSpecifier in instructionSetParams) 121 | { 122 | instructionSet = instructionSetSpecifier.Substring(1); 123 | 124 | bool enabled = instructionSetSpecifier[0] == '+' ? true : false; 125 | if (enabled) 126 | { 127 | if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) 128 | throw new CommandLineException(string.Format(mustNotBeMessage, instructionSet)); 129 | } 130 | else 131 | { 132 | if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) 133 | throw new CommandLineException(string.Format(mustNotBeMessage, instructionSet)); 134 | } 135 | } 136 | } 137 | 138 | // When we are in a fully AOT scenario, such as NativeAOT, then Vector 139 | // can be directly part of the supported ISAs. This is because we are targeting 140 | // an exact machine and we won't have any risk of a more capable machine supporting 141 | // a larger Vector and needing to invalidate any methods pre-compiled targeting 142 | // smaller sizes. 143 | // 144 | // However, when we are in a partial AOT scenario, such as Crossgen2, then 145 | // Vector must only appear in the optimistic set since the size supported 146 | // by the pre-compiled code may be smaller (or larger) than what is actually 147 | // supported at runtime. 148 | 149 | bool skipAddingVectorT = isVectorTOptimistic; 150 | 151 | instructionSetSupportBuilder.ComputeInstructionSetFlags(maxVectorTBitWidth, skipAddingVectorT, out var supportedInstructionSet, out var unsupportedInstructionSet, 152 | (string specifiedInstructionSet, string impliedInstructionSet) => 153 | throw new CommandLineException(string.Format(invalidImplicationMessage, specifiedInstructionSet, impliedInstructionSet))); 154 | 155 | // Due to expansion by implication, the optimistic set is most often a pure superset of the supported set 156 | // 157 | // However, there are some gaps in cases like Arm64 neon where none of the optimistic sets imply it. Likewise, 158 | // the optimistic set would be missing the explicitly unsupported sets. So we effectively clone the list and 159 | // tack on the additional optimistic bits after. This ensures the optimistic set remains an accurate superset 160 | InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(instructionSetSupportBuilder); 161 | 162 | // Optimistically assume some instruction sets are present. 163 | if (allowOptimistic && (targetArchitecture == TargetArchitecture.X86 || targetArchitecture == TargetArchitecture.X64)) 164 | { 165 | // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. 166 | // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as 167 | // the presence of those makes otherwise acceptable code be unusable on hardware which does not support VEX encodings. 168 | // 169 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication 170 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); 171 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); 172 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe"); 173 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); 174 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); 175 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("serialize"); 176 | 177 | // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings 178 | Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); 179 | if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) 180 | { 181 | // TODO: Enable optimistic usage of AVX2 once we validate it doesn't break Vector usage 182 | // optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx2"); 183 | 184 | if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX2)) 185 | { 186 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); 187 | } 188 | 189 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); 190 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); 191 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); 192 | } 193 | 194 | Debug.Assert(InstructionSet.X64_AVX512F == InstructionSet.X86_AVX512F); 195 | if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512F)) 196 | { 197 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512F_VL)); 198 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512BW)); 199 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512BW_VL)); 200 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512CD)); 201 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512CD_VL)); 202 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512DQ)); 203 | Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512DQ_VL)); 204 | 205 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vbmi"); 206 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vbmi_vl"); 207 | } 208 | } 209 | else if (targetArchitecture == TargetArchitecture.ARM64) 210 | { 211 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); 212 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); 213 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); 214 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); 215 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); 216 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("dotprod"); 217 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rdma"); 218 | optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); 219 | } 220 | 221 | // Vector can always be part of the optimistic set, we only want to optionally exclude it from the supported set 222 | optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(maxVectorTBitWidth, skipAddingVectorT: false, out var optimisticInstructionSet, out _, 223 | (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); 224 | optimisticInstructionSet.Remove(unsupportedInstructionSet); 225 | optimisticInstructionSet.Add(supportedInstructionSet); 226 | 227 | return new InstructionSetSupport(supportedInstructionSet, 228 | unsupportedInstructionSet, 229 | optimisticInstructionSet, 230 | InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(targetArchitecture), 231 | targetArchitecture); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/bflat/PerfWatch.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System; 18 | using System.Diagnostics; 19 | 20 | internal struct PerfWatch : IDisposable 21 | { 22 | private Stopwatch _sw; 23 | private string _name; 24 | 25 | private static bool IsEnabled { get; } = Environment.GetEnvironmentVariable("BFLAT_TIMINGS") == "1"; 26 | 27 | public PerfWatch(string name) 28 | { 29 | if (IsEnabled) 30 | { 31 | _name = name; 32 | _sw = Stopwatch.StartNew(); 33 | } 34 | else 35 | { 36 | _name = null; 37 | _sw = null; 38 | } 39 | } 40 | 41 | public void Complete() 42 | { 43 | if (_sw != null) 44 | { 45 | Console.WriteLine($"{_name}: {_sw.Elapsed}"); 46 | } 47 | } 48 | 49 | public void Dispose() => Complete(); 50 | } 51 | -------------------------------------------------------------------------------- /src/bflat/Program.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System; 18 | using System.CommandLine; 19 | using System.CommandLine.Builder; 20 | using System.CommandLine.Help; 21 | using System.CommandLine.Parsing; 22 | using System.Diagnostics; 23 | using System.IO; 24 | using System.Reflection; 25 | 26 | class Program 27 | { 28 | private readonly static Option InfoOption = new Option("--info", "Show .NET information"); 29 | 30 | private static int Main(string[] args) 31 | { 32 | string exeName = Environment.ProcessPath; 33 | if (exeName != null) 34 | { 35 | FileSystemInfo actualExeInfo = File.ResolveLinkTarget(exeName, returnFinalTarget: true); 36 | if (actualExeInfo != null) 37 | { 38 | Process p = Process.Start(actualExeInfo.FullName, args); 39 | p.WaitForExit(); 40 | return p.ExitCode; 41 | } 42 | } 43 | 44 | using PerfWatch total = new PerfWatch("Total"); 45 | 46 | var root = new RootCommand( 47 | "Bflat C# compiler\n" + 48 | "Copyright (c) 2021-2022 Michal Strehovsky\n" + 49 | "https://flattened.net\n") 50 | { 51 | BuildCommand.Create(), 52 | ILBuildCommand.Create(), 53 | InfoOption, 54 | }; 55 | root.SetHandler(ctx => 56 | { 57 | if (ctx.ParseResult.GetValueForOption(InfoOption)) 58 | { 59 | foreach (var attr in Assembly.GetExecutingAssembly().GetCustomAttributes()) 60 | { 61 | string friendlyName = attr.Key switch 62 | { 63 | "BflatRuntimeVersion" => ".NET Runtime", 64 | "MicrosoftCodeAnalysisCSharpVersion" => "C# Compiler", 65 | _ => null, 66 | }; 67 | 68 | if (friendlyName != null) 69 | { 70 | Console.WriteLine($"{friendlyName} Version:"); 71 | Console.WriteLine($" {attr.Value}"); 72 | } 73 | } 74 | } 75 | else 76 | { 77 | ctx.HelpBuilder.Write(root, Console.Out); 78 | } 79 | }); 80 | 81 | Parser parser = new CommandLineBuilder(root) 82 | .UseVersionOption("-v") 83 | .UseParseErrorReporting() 84 | .UseHelp() 85 | .Build(); 86 | 87 | #if DEBUG 88 | return parser.Invoke(args); 89 | #else 90 | try 91 | { 92 | return parser.Invoke(args); 93 | } 94 | catch (Exception e) 95 | { 96 | Console.Error.WriteLine("Error: " + e.Message); 97 | Console.Error.WriteLine(e.ToString()); 98 | return 1; 99 | } 100 | #endif 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/bflat/bflat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exe 6 | net8.0 7 | true 8 | 42.42.42.42 9 | en-US 10 | 11 | 12 | 13 | 8.0.2-rtm.24317.2 14 | https://github.com/bflattened/runtime/releases/download/ 15 | 16 | 1.3 17 | https://github.com/bflattened/blobs/releases/download/ 18 | 19 | 4.8.0-3.final 20 | 21 | 22 | 23 | 24 | <_Parameter1>BflatRuntimeVersion 25 | <_Parameter2>$(RuntimeVersion) 26 | 27 | 28 | <_Parameter1>MicrosoftCodeAnalysisCSharpVersion 29 | <_Parameter2>$(MicrosoftCodeAnalysisCSharpVersion) 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | windows-x64 41 | linux-glibc-x64 42 | $(BaseIntermediateOutputPath) 43 | 44 | 45 | 46 | 47 | windows 48 | x64 49 | 50 | 51 | windows 52 | arm64 53 | 54 | 55 | linux 56 | x64 57 | 58 | 59 | linux 60 | arm64 61 | 62 | 63 | 64 | 65 | 66 | windows\x64\ 67 | 68 | 69 | windows\arm64\ 70 | 71 | 72 | windows\x86\ 73 | 74 | 75 | linux\x64\glibc\ 76 | 77 | 78 | linux\arm64\glibc 79 | 80 | 81 | linux\arm64\bionic 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 100 | 101 | 105 | 106 | 110 | 111 | 112 | 116 | 117 | 121 | 122 | 123 | 126 | 127 | 129 | 130 | 132 | 133 | 134 | 137 | 138 | 140 | 141 | 142 | 143 | 144 | 146 | 147 | 148 | 151 | 152 | 154 | 155 | 157 | 158 | 159 | 162 | 163 | 165 | 166 | 169 | 170 | 172 | 173 | 174 | 177 | 178 | 180 | 181 | 183 | 184 | 185 | 189 | 190 | 191 | 192 | 193 | $(LayoutsDirectory)%(SupportedHost.Identity)\ 194 | $(LayoutsDirectory)obj\%(SupportedHost.Identity)\ 195 | bflat 196 | bflat.exe 197 | 198 | 199 | 200 | 201 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 224 | 225 | 226 | 227 | 229 | 231 | 233 | 234 | 236 | 238 | 240 | 242 | 244 | 246 | 248 | 250 | 251 | 252 | 253 | 254 | 255 | $(BuildDependsOn);CreateCompilerLayout 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /src/debloat/Program.cs: -------------------------------------------------------------------------------- 1 | // bflat C# compiler 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Collections.Immutable; 20 | using System.IO; 21 | using System.Reflection.Metadata; 22 | using System.Reflection.PortableExecutable; 23 | 24 | Merge(args[0]); 25 | 26 | static void Merge(string directory) 27 | { 28 | foreach (string subDirectory in Directory.EnumerateDirectories(directory)) 29 | Merge(subDirectory); 30 | 31 | Dictionary> candidates = new Dictionary>(); 32 | int expectedFiles = 0; 33 | foreach (string subDirectory in Directory.EnumerateDirectories(directory)) 34 | { 35 | // x86 Windows is hacked up 36 | if (subDirectory.Contains("x86") && subDirectory.Contains("windows")) 37 | continue; 38 | 39 | expectedFiles++; 40 | foreach (var f in Directory.EnumerateFiles(subDirectory, "*.dll")) 41 | { 42 | string key = Path.GetFileName(f); 43 | if (!candidates.TryGetValue(key, out List list)) 44 | candidates.Add(key, list = new List()); 45 | list.Add(f); 46 | } 47 | } 48 | 49 | foreach (var maybeSameFiles in candidates.Values) 50 | { 51 | if (maybeSameFiles.Count != expectedFiles) 52 | continue; 53 | 54 | bool allSame = true; 55 | ReadOnlySpan expected = SanitizedAssemblyBytes(maybeSameFiles[0]); 56 | for (int i = 1; i < maybeSameFiles.Count; i++) 57 | { 58 | ReadOnlySpan actual = SanitizedAssemblyBytes(maybeSameFiles[i]); 59 | if (!expected.SequenceEqual(actual)) 60 | { 61 | allSame = false; 62 | break; 63 | } 64 | } 65 | 66 | if (allSame) 67 | { 68 | File.Move(maybeSameFiles[0], Path.Combine(directory, Path.GetFileName(maybeSameFiles[0]))); 69 | string pdbFile = Path.ChangeExtension(maybeSameFiles[0], "pdb"); 70 | if (File.Exists(pdbFile)) 71 | File.Move(pdbFile, Path.Combine(directory, Path.GetFileName(pdbFile))); 72 | 73 | for (int i = 1; i < maybeSameFiles.Count; i++) 74 | { 75 | File.Delete(maybeSameFiles[i]); 76 | pdbFile = Path.ChangeExtension(maybeSameFiles[i], "pdb"); 77 | if (File.Exists(pdbFile)) 78 | File.Delete(pdbFile); 79 | } 80 | } 81 | } 82 | } 83 | 84 | static byte[] SanitizedAssemblyBytes(string fileName) 85 | { 86 | byte[] b = File.ReadAllBytes(fileName); 87 | Span span = b; 88 | PEReader perdr = new PEReader(ImmutableArray.Create(b)); 89 | span.Slice(perdr.PEHeaders.CoffHeaderStartOffset + 4, 4).Clear(); 90 | MetadataReader mdrdr = perdr.GetMetadataReader(); 91 | Guid mvid = mdrdr.GetGuid(mdrdr.GetModuleDefinition().Mvid); 92 | span.Slice(span.IndexOf(mvid.ToByteArray()), 16).Clear(); 93 | foreach (var ddentry in perdr.ReadDebugDirectory()) 94 | span.Slice(ddentry.DataPointer, ddentry.DataSize).Clear(); 95 | if (perdr.PEHeaders.TryGetDirectoryOffset(perdr.PEHeaders.PEHeader.DebugTableDirectory, out int debugDir)) 96 | span.Slice(debugDir, perdr.PEHeaders.PEHeader.DebugTableDirectory.Size).Clear(); 97 | return b; 98 | } -------------------------------------------------------------------------------- /src/debloat/debloat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/zerolib/Internal/Runtime/CompilerHelpers/InteropHelpers.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System; 18 | using System.Runtime.InteropServices; 19 | 20 | namespace Internal.Runtime.CompilerHelpers 21 | { 22 | internal unsafe static class InteropHelpers 23 | { 24 | private static IntPtr ResolvePInvoke(MethodFixupCell* pCell) 25 | { 26 | if (pCell->Target != default) 27 | return pCell->Target; 28 | 29 | return ResolvePInvokeSlow(pCell); 30 | } 31 | 32 | private static IntPtr ResolvePInvokeSlow(MethodFixupCell* pCell) 33 | { 34 | ModuleFixupCell* pModuleCell = pCell->Module; 35 | if (pModuleCell->Handle == default) 36 | { 37 | #if WINDOWS 38 | pModuleCell->Handle = LoadLibraryA(pModuleCell->ModuleName); 39 | 40 | [DllImport("kernel32"), SuppressGCTransition] 41 | extern static IntPtr LoadLibraryA(IntPtr name); 42 | #elif LINUX 43 | pModuleCell->Handle = SystemNative_LoadLibrary(pModuleCell->ModuleName); 44 | 45 | [DllImport("libSystem.Native"), SuppressGCTransition] 46 | extern static IntPtr SystemNative_LoadLibrary(IntPtr name); 47 | #endif 48 | if (pModuleCell->Handle == default) 49 | Environment.FailFast(null); 50 | } 51 | 52 | #if WINDOWS 53 | pCell->Target = GetProcAddress(pModuleCell->Handle, pCell->MethodName); 54 | 55 | [DllImport("kernel32"), SuppressGCTransition] 56 | extern static IntPtr GetProcAddress(IntPtr hModule, IntPtr name); 57 | #elif LINUX 58 | pCell->Target = SystemNative_GetProcAddress(pModuleCell->Handle, pCell->MethodName); 59 | 60 | [DllImport("libSystem.Native"), SuppressGCTransition] 61 | extern static IntPtr SystemNative_GetProcAddress(IntPtr hModule, IntPtr name); 62 | #endif 63 | 64 | if (pCell->Target == default) 65 | Environment.FailFast(null); 66 | 67 | return pCell->Target; 68 | } 69 | 70 | [StructLayout(LayoutKind.Sequential)] 71 | internal struct ModuleFixupCell 72 | { 73 | public IntPtr Handle; 74 | public IntPtr ModuleName; 75 | public IntPtr CallingAssemblyType; 76 | public uint DllImportSearchPathAndCookie; 77 | } 78 | 79 | [StructLayout(LayoutKind.Sequential)] 80 | internal struct MethodFixupCell 81 | { 82 | public IntPtr Target; 83 | public IntPtr MethodName; 84 | public ModuleFixupCell* Module; 85 | private int Flags; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/zerolib/Internal/Startup.Efi.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if UEFI 18 | 19 | using System; 20 | using System.Runtime; 21 | using System.Runtime.CompilerServices; 22 | using System.Runtime.InteropServices; 23 | 24 | namespace Internal.Runtime.CompilerHelpers 25 | { 26 | unsafe partial class StartupCodeHelpers 27 | { 28 | [RuntimeImport("*", "__managed__Main")] 29 | [MethodImpl(MethodImplOptions.InternalCall)] 30 | static extern int ManagedMain(int argc, char** argv); 31 | 32 | [RuntimeExport("EfiMain")] 33 | static long EfiMain(IntPtr imageHandle, EFI_SYSTEM_TABLE* systemTable) 34 | { 35 | SetEfiSystemTable(systemTable); 36 | ManagedMain(0, null); 37 | 38 | while (true) ; 39 | } 40 | 41 | internal static unsafe void InitializeCommandLineArgsW(int argc, char** argv) 42 | { 43 | // argc and argv are garbage because EfiMain didn't pass any 44 | } 45 | 46 | internal static string[] GetMainMethodArguments() 47 | { 48 | return new string[0]; 49 | } 50 | } 51 | 52 | [StructLayout(LayoutKind.Sequential)] 53 | struct EFI_HANDLE 54 | { 55 | private IntPtr _handle; 56 | } 57 | 58 | [StructLayout(LayoutKind.Sequential)] 59 | unsafe readonly struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 60 | { 61 | private readonly IntPtr _pad0; 62 | public readonly delegate* unmanaged OutputString; 63 | private readonly IntPtr _pad1; 64 | private readonly IntPtr _pad2; 65 | private readonly IntPtr _pad3; 66 | public readonly delegate* unmanaged SetAttribute; 67 | private readonly IntPtr _pad4; 68 | public readonly delegate* unmanaged SetCursorPosition; 69 | } 70 | 71 | [StructLayout(LayoutKind.Sequential)] 72 | readonly struct EFI_INPUT_KEY 73 | { 74 | public readonly ushort ScanCode; 75 | public readonly ushort UnicodeChar; 76 | } 77 | 78 | [StructLayout(LayoutKind.Sequential)] 79 | unsafe readonly struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL 80 | { 81 | private readonly IntPtr _pad0; 82 | public readonly delegate* unmanaged ReadKeyStroke; 83 | } 84 | 85 | [StructLayout(LayoutKind.Sequential)] 86 | readonly struct EFI_TABLE_HEADER 87 | { 88 | public readonly ulong Signature; 89 | public readonly uint Revision; 90 | public readonly uint HeaderSize; 91 | public readonly uint Crc32; 92 | public readonly uint Reserved; 93 | } 94 | 95 | [StructLayout(LayoutKind.Sequential)] 96 | unsafe readonly struct EFI_SYSTEM_TABLE 97 | { 98 | public readonly EFI_TABLE_HEADER Hdr; 99 | public readonly char* FirmwareVendor; 100 | public readonly uint FirmwareRevision; 101 | public readonly EFI_HANDLE ConsoleInHandle; 102 | public readonly EFI_SIMPLE_TEXT_INPUT_PROTOCOL* ConIn; 103 | public readonly EFI_HANDLE ConsoleOutHandle; 104 | public readonly EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* ConOut; 105 | public readonly EFI_HANDLE StandardErrorHandle; 106 | public readonly EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* StdErr; 107 | public readonly EFI_RUNTIME_SERVICES* RuntimeServices; 108 | public readonly EFI_BOOT_SERVICES* BootServices; 109 | } 110 | 111 | [StructLayout(LayoutKind.Sequential)] 112 | struct EFI_TIME 113 | { 114 | public ushort Year; 115 | public byte Month; 116 | public byte Day; 117 | public byte Hour; 118 | public byte Minute; 119 | public byte Second; 120 | public byte Pad1; 121 | public uint Nanosecond; 122 | public short TimeZone; 123 | public byte Daylight; 124 | public byte PAD2; 125 | } 126 | 127 | [StructLayout(LayoutKind.Sequential)] 128 | struct EFI_TIME_CAPABILITIES 129 | { 130 | public uint Resolution; 131 | public uint Accuracy; 132 | public byte SetsToZero; 133 | } 134 | 135 | [StructLayout(LayoutKind.Sequential)] 136 | unsafe readonly struct EFI_RUNTIME_SERVICES 137 | { 138 | public readonly EFI_TABLE_HEADER Hdr; 139 | public readonly delegate* unmanaged GetTime; 140 | } 141 | 142 | [StructLayout(LayoutKind.Sequential)] 143 | unsafe readonly struct EFI_BOOT_SERVICES 144 | { 145 | readonly EFI_TABLE_HEADER Hdr; 146 | private readonly void* pad0; 147 | private readonly void* pad1; 148 | private readonly void* pad2; 149 | private readonly void* pad3; 150 | private readonly void* pad4; 151 | public readonly delegate* unmanaged AllocatePool; 152 | private readonly void* pad6; 153 | private readonly void* pad7; 154 | private readonly void* pad8; 155 | private readonly void* pad9; 156 | private readonly void* pad10; 157 | private readonly void* pad11; 158 | private readonly void* pad12; 159 | private readonly void* pad13; 160 | private readonly void* pad14; 161 | private readonly void* pad15; 162 | private readonly void* pad16; 163 | private readonly void* pad17; 164 | private readonly void* pad18; 165 | private readonly void* pad19; 166 | private readonly void* pad20; 167 | private readonly void* pad21; 168 | private readonly void* pad22; 169 | private readonly void* pad23; 170 | private readonly void* pad24; 171 | private readonly void* pad25; 172 | private readonly void* pad26; 173 | private readonly void* pad27; 174 | public readonly delegate* unmanaged Stall; 175 | } 176 | } 177 | 178 | #endif 179 | -------------------------------------------------------------------------------- /src/zerolib/Internal/Startup.Unix.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if LINUX 18 | 19 | using System; 20 | using System.Runtime; 21 | using System.Runtime.InteropServices; 22 | 23 | namespace Internal.Runtime.CompilerHelpers 24 | { 25 | unsafe partial class StartupCodeHelpers 26 | { 27 | static int s_argc; 28 | static sbyte** s_argv; 29 | 30 | internal static unsafe void InitializeCommandLineArgs(int argc, sbyte** argv) 31 | { 32 | s_argc = argc; 33 | s_argv = argv; 34 | } 35 | 36 | internal static string[] GetMainMethodArguments() 37 | { 38 | string[] args = new string[s_argc - 1]; 39 | for (int i = 1; i < s_argc; ++i) 40 | { 41 | args[i - 1] = new string(s_argv[i]); 42 | } 43 | 44 | return args; 45 | } 46 | } 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/zerolib/Internal/Startup.Windows.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if WINDOWS 18 | 19 | using System; 20 | using System.Runtime; 21 | using System.Runtime.InteropServices; 22 | 23 | namespace Internal.Runtime.CompilerHelpers 24 | { 25 | unsafe partial class StartupCodeHelpers 26 | { 27 | internal static unsafe void InitializeCommandLineArgsW(int argc, char** argv) 28 | { 29 | // argc and argv are a lie because CRT didn't start the process on Windows 30 | } 31 | 32 | internal static string[] GetMainMethodArguments() 33 | { 34 | int argc; 35 | char** argv = CommandLineToArgvW(GetCommandLineW(), &argc); 36 | 37 | [DllImport("kernel32"), SuppressGCTransition] 38 | static extern char* GetCommandLineW(); 39 | 40 | [DllImport("shell32"), SuppressGCTransition] 41 | static extern char** CommandLineToArgvW(char* lpCmdLine, int* pNumArgs); 42 | 43 | string[] args = new string[argc - 1]; 44 | for (int i = 1; i < argc; ++i) 45 | { 46 | args[i - 1] = new string(argv[i]); 47 | } 48 | 49 | return args; 50 | } 51 | } 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/zerolib/Internal/Stubs.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System; 18 | using System.Runtime; 19 | using System.Runtime.InteropServices; 20 | using System.Runtime.CompilerServices; 21 | 22 | namespace System.Runtime 23 | { 24 | internal sealed class RuntimeExportAttribute : Attribute 25 | { 26 | public RuntimeExportAttribute(string entry) { } 27 | } 28 | 29 | internal sealed class RuntimeImportAttribute : Attribute 30 | { 31 | public RuntimeImportAttribute(string lib) { } 32 | public RuntimeImportAttribute(string lib, string entry) { } 33 | } 34 | 35 | internal unsafe struct MethodTable 36 | { 37 | internal ushort _usComponentSize; 38 | private ushort _usFlags; 39 | internal uint _uBaseSize; 40 | internal MethodTable* _relatedType; 41 | private ushort _usNumVtableSlots; 42 | private ushort _usNumInterfaces; 43 | private uint _uHashCode; 44 | } 45 | } 46 | 47 | namespace Internal.Runtime.CompilerHelpers 48 | { 49 | partial class ThrowHelpers 50 | { 51 | static void ThrowIndexOutOfRangeException() => Environment.FailFast(null); 52 | static void ThrowDivideByZeroException() => Environment.FailFast(null); 53 | static void ThrowPlatformNotSupportedException() => Environment.FailFast(null); 54 | } 55 | 56 | // A class that the compiler looks for that has helpers to initialize the 57 | // process. The compiler can gracefully handle the helpers not being present, 58 | // but the class itself being absent is unhandled. Let's add an empty class. 59 | unsafe partial class StartupCodeHelpers 60 | { 61 | // A couple symbols the generated code will need we park them in this class 62 | // for no particular reason. These aid in transitioning to/from managed code. 63 | // Since we don't have a GC, the transition is a no-op. 64 | [RuntimeExport("RhpReversePInvoke")] 65 | static void RhpReversePInvoke(IntPtr frame) { } 66 | [RuntimeExport("RhpReversePInvokeReturn")] 67 | static void RhpReversePInvokeReturn(IntPtr frame) { } 68 | [RuntimeExport("RhpPInvoke")] 69 | static void RhpPInvoke(IntPtr frame) { } 70 | [RuntimeExport("RhpPInvokeReturn")] 71 | static void RhpPInvokeReturn(IntPtr frame) { } 72 | [RuntimeExport("RhpGcPoll")] 73 | static void RhpGcPoll() { } 74 | 75 | [RuntimeExport("RhpFallbackFailFast")] 76 | static void RhpFallbackFailFast() { Environment.FailFast(null); } 77 | 78 | [RuntimeExport("RhpNewFast")] 79 | static unsafe void* RhpNewFast(MethodTable* pMT) 80 | { 81 | MethodTable** result = AllocObject(pMT->_uBaseSize); 82 | *result = pMT; 83 | return result; 84 | } 85 | 86 | [RuntimeExport("RhpNewArray")] 87 | static unsafe void* RhpNewArray(MethodTable* pMT, int numElements) 88 | { 89 | if (numElements < 0) 90 | Environment.FailFast(null); 91 | 92 | MethodTable** result = AllocObject((uint)(pMT->_uBaseSize + numElements * pMT->_usComponentSize)); 93 | *result = pMT; 94 | *(int*)(result + 1) = numElements; 95 | return result; 96 | } 97 | 98 | internal struct ArrayElement 99 | { 100 | public object Value; 101 | } 102 | 103 | [RuntimeExport("RhpStelemRef")] 104 | public static unsafe void StelemRef(Array array, nint index, object obj) 105 | { 106 | ref object element = ref Unsafe.As(array)[index].Value; 107 | 108 | MethodTable* elementType = array.m_pMethodTable->_relatedType; 109 | 110 | if (obj == null) 111 | goto assigningNull; 112 | 113 | if (elementType != obj.m_pMethodTable) 114 | Environment.FailFast(null); /* covariance */ 115 | 116 | doWrite: 117 | element = obj; 118 | return; 119 | 120 | assigningNull: 121 | element = null; 122 | return; 123 | } 124 | 125 | [RuntimeExport("RhpCheckedAssignRef")] 126 | public static unsafe void RhpCheckedAssignRef(void** dst, void* r) 127 | { 128 | *dst = r; 129 | } 130 | 131 | [RuntimeExport("RhpAssignRef")] 132 | public static unsafe void RhpAssignRef(void** dst, void* r) 133 | { 134 | *dst = r; 135 | } 136 | 137 | static unsafe MethodTable** AllocObject(uint size) 138 | { 139 | #if WINDOWS 140 | [DllImport("kernel32"), SuppressGCTransition] 141 | static extern MethodTable** LocalAlloc(uint flags, uint size); 142 | MethodTable** result = LocalAlloc(0x40, size); 143 | #elif LINUX 144 | [DllImport("libSystem.Native"), SuppressGCTransition] 145 | static extern MethodTable** SystemNative_Malloc(nuint size); 146 | MethodTable** result = SystemNative_Malloc(size); 147 | #elif UEFI 148 | MethodTable** result; 149 | if (EfiSystemTable->BootServices->AllocatePool(2 /* LoaderData*/, (nint)size, (void**)&result) != 0) 150 | result = null; 151 | #else 152 | #error Nope 153 | #endif 154 | 155 | if (result == null) 156 | Environment.FailFast(null); 157 | 158 | return result; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/zerolib/README.md: -------------------------------------------------------------------------------- 1 | # ZeroLib minimal runtime library 2 | 3 | There are two guiding principles: 4 | 5 | 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). 6 | 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. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /src/zerolib/System/Array.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public abstract class Array 20 | { 21 | private readonly int _length; 22 | 23 | public int Length => _length; 24 | } 25 | 26 | class Array : Array { } 27 | } 28 | -------------------------------------------------------------------------------- /src/zerolib/System/Attribute.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public abstract class Attribute { } 20 | 21 | public enum AttributeTargets { } 22 | 23 | public sealed class AttributeUsageAttribute : Attribute 24 | { 25 | //Constructors 26 | public AttributeUsageAttribute(AttributeTargets validOn) 27 | { 28 | } 29 | 30 | public bool AllowMultiple 31 | { 32 | get { return false; } 33 | set { } 34 | } 35 | 36 | public bool Inherited 37 | { 38 | get { return false; } 39 | set { } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/zerolib/System/Console.Efi.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if UEFI 18 | 19 | using Internal.Runtime.CompilerHelpers; 20 | 21 | namespace System 22 | { 23 | public static unsafe partial class Console 24 | { 25 | public static unsafe void Write(char c) 26 | { 27 | int cc = c; 28 | EfiSystemTable->ConOut->OutputString(EfiSystemTable->ConOut, (char*)&cc); 29 | } 30 | 31 | public static unsafe ConsoleColor ForegroundColor 32 | { 33 | set 34 | { 35 | EfiSystemTable->ConOut->SetAttribute(EfiSystemTable->ConOut, (uint)value); 36 | } 37 | } 38 | 39 | public static void SetCursorPosition(int x, int y) 40 | { 41 | EfiSystemTable->ConOut->SetCursorPosition( 42 | EfiSystemTable->ConOut, 43 | (uint)x, 44 | (uint)y); 45 | } 46 | 47 | static char s_keyBuffer; 48 | static ushort s_scanCodeBuffer; 49 | 50 | private static unsafe bool FillBuffer() 51 | { 52 | if (s_scanCodeBuffer == 0) 53 | { 54 | EFI_INPUT_KEY key; 55 | if (EfiSystemTable->ConIn->ReadKeyStroke(EfiSystemTable->ConIn, &key) == 0) 56 | { 57 | s_keyBuffer = (char)key.UnicodeChar; 58 | s_scanCodeBuffer = key.ScanCode; 59 | return true; 60 | } 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | public static bool KeyAvailable => FillBuffer(); 67 | 68 | public static ConsoleKeyInfo ReadKey(bool intercept) 69 | { 70 | if (FillBuffer()) 71 | { 72 | ConsoleKey key = s_scanCodeBuffer switch 73 | { 74 | 1 => ConsoleKey.UpArrow, 75 | 2 => ConsoleKey.DownArrow, 76 | 3 => ConsoleKey.RightArrow, 77 | 4 => ConsoleKey.LeftArrow, 78 | _ => default(ConsoleKey), 79 | }; 80 | s_scanCodeBuffer = 0; 81 | return new ConsoleKeyInfo(s_keyBuffer, key, false, false, false); 82 | } 83 | return default; 84 | } 85 | 86 | public static unsafe void SetWindowSize(int x, int y) 87 | { 88 | } 89 | 90 | public static void SetBufferSize(int x, int y) 91 | { 92 | } 93 | 94 | public static unsafe string Title 95 | { 96 | set 97 | { 98 | _ = value; 99 | } 100 | } 101 | 102 | public static unsafe bool CursorVisible 103 | { 104 | set 105 | { 106 | _ = value; 107 | } 108 | } 109 | } 110 | } 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /src/zerolib/System/Console.Unix.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if LINUX 18 | 19 | using System.Runtime.InteropServices; 20 | 21 | namespace System 22 | { 23 | public static unsafe partial class Console 24 | { 25 | public static unsafe string Title 26 | { 27 | set 28 | { 29 | _ = value; 30 | } 31 | } 32 | 33 | public static unsafe bool CursorVisible 34 | { 35 | set 36 | { 37 | _ = value; 38 | } 39 | } 40 | 41 | public static unsafe void SetWindowSize(int x, int y) 42 | { 43 | } 44 | 45 | public static void SetBufferSize(int x, int y) 46 | { 47 | } 48 | 49 | public static ConsoleColor ForegroundColor 50 | { 51 | set 52 | { 53 | ConsoleColor adjusted = value; 54 | if (value >= ConsoleColor.DarkBlue && value <= ConsoleColor.Gray) 55 | adjusted = value - ConsoleColor.DarkBlue + ConsoleColor.Blue; 56 | int colorCode = adjusted switch 57 | { 58 | ConsoleColor.Black => 30, 59 | ConsoleColor.Blue => 34, 60 | ConsoleColor.Green => 32, 61 | ConsoleColor.Cyan => 36, 62 | ConsoleColor.Red => 31, 63 | ConsoleColor.Magenta => 35, 64 | ConsoleColor.Yellow => 33, 65 | _ => 37, 66 | }; 67 | byte* pBuf = stackalloc byte[16]; 68 | pBuf[0] = 0x1B; 69 | pBuf[1] = (byte)'['; 70 | byte* pCur = Append(&pBuf[2], colorCode); 71 | *pCur++ = (byte)'m'; 72 | SystemNative_Log(pBuf, (int)(pCur - pBuf)); 73 | } 74 | } 75 | 76 | public static void SetCursorPosition(int x, int y) 77 | { 78 | byte* pBuf = stackalloc byte[32]; 79 | pBuf[0] = 0x1B; 80 | pBuf[1] = (byte)'['; 81 | byte* cur = Append(&pBuf[2], y); 82 | *cur++ = (byte)';'; 83 | cur = Append(cur, x); 84 | *cur++ = (byte)'H'; 85 | SystemNative_Log(pBuf, (int)(cur - pBuf)); 86 | } 87 | 88 | static byte* Append(byte* b, int x) 89 | { 90 | if (x >= 10) 91 | b = Append(b, x / 10); 92 | *b = (byte)((x % 10) + '0'); 93 | return ++b; 94 | } 95 | 96 | [DllImport("libSystem.Native"), SuppressGCTransition] 97 | private static extern void SystemNative_Log(void* pBuffer, int length); 98 | 99 | public static void Write(char c) 100 | { 101 | if (c <= 0x7F) 102 | { 103 | SystemNative_Log(&c, 1); 104 | } 105 | else if (c <= 0x7FF) 106 | { 107 | ushort twoByte; 108 | byte* pOut = (byte*)&twoByte; 109 | *pOut++ = (byte)(0xC0 | (c >> 6)); 110 | *pOut++ = (byte)(0x80 | (c & 0x3F)); 111 | SystemNative_Log(&twoByte, 2); 112 | } 113 | else 114 | { 115 | int threeByte; 116 | byte* pOut = (byte*)&threeByte; 117 | *pOut++ = (byte)(0xE0 | (c >> 12)); 118 | *pOut++ = (byte)(0x80 | ((c >> 6) & 0x3F)); 119 | *pOut++ = (byte)(0x80 | (c & 0x3F)); 120 | SystemNative_Log(&threeByte, 3); 121 | } 122 | } 123 | 124 | public static bool KeyAvailable => StdInReader.KeyAvailable(); 125 | 126 | public static ConsoleKeyInfo ReadKey(bool intercept) => StdInReader.ReadKey(); 127 | 128 | static class StdInReader 129 | { 130 | static StdInReader() 131 | { 132 | SystemNative_InitializeTerminalAndSignalHandling(); 133 | 134 | [DllImport("libSystem.Native"), SuppressGCTransition] 135 | static extern int SystemNative_InitializeTerminalAndSignalHandling(); 136 | } 137 | 138 | private static StdInBuffer s_buffer; 139 | 140 | struct StdInBuffer 141 | { 142 | public const int Size = 256; 143 | public int StartIndex; 144 | public int EndIndex; 145 | public bool Empty => StartIndex >= EndIndex; 146 | public fixed char Chars[Size]; 147 | } 148 | 149 | public static bool KeyAvailable() 150 | { 151 | return SystemNative_StdinReady() != 0; 152 | 153 | [DllImport("libSystem.Native"), SuppressGCTransition] 154 | static extern int SystemNative_StdinReady(); 155 | } 156 | 157 | public static ConsoleKeyInfo ReadKey() 158 | { 159 | SystemNative_InitializeConsoleBeforeRead(1, 0); 160 | 161 | [DllImport("libSystem.Native"), SuppressGCTransition] 162 | static extern void SystemNative_InitializeConsoleBeforeRead(byte minChars, byte decisecondsTimeout); 163 | 164 | try 165 | { 166 | if (s_buffer.Empty) 167 | { 168 | byte* bufPtr = stackalloc byte[StdInBuffer.Size]; 169 | int result = SystemNative_ReadStdin(bufPtr, StdInBuffer.Size); 170 | 171 | [DllImport("libSystem.Native"), SuppressGCTransition] 172 | static extern unsafe int SystemNative_ReadStdin(byte* buffer, int bufferSize); 173 | 174 | if (result <= 0) 175 | { 176 | return default; 177 | } 178 | s_buffer.StartIndex = 0; 179 | s_buffer.EndIndex = result; 180 | for (int i = 0; i < result; i++) 181 | { 182 | char c = (char)bufPtr[i]; 183 | if (c > 0x7F) 184 | Environment.FailFast(null); 185 | s_buffer.Chars[i] = c; 186 | } 187 | } 188 | 189 | if (s_buffer.EndIndex - s_buffer.StartIndex >= 3 && 190 | s_buffer.Chars[s_buffer.StartIndex] == 0x1B && 191 | s_buffer.Chars[s_buffer.StartIndex + 1] == '[') 192 | { 193 | char code = s_buffer.Chars[s_buffer.StartIndex + 2]; 194 | s_buffer.StartIndex += 3; 195 | switch (code) 196 | { 197 | case 'A': 198 | return new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false); 199 | case 'B': 200 | return new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false); 201 | case 'C': 202 | return new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false); 203 | case 'D': 204 | return new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false); 205 | default: 206 | Environment.FailFast(null); 207 | return default; 208 | } 209 | } 210 | 211 | return default; 212 | 213 | } 214 | finally 215 | { 216 | SystemNative_UninitializeConsoleAfterRead(); 217 | 218 | [DllImport("libSystem.Native"), SuppressGCTransition] 219 | static extern void SystemNative_UninitializeConsoleAfterRead(); 220 | } 221 | } 222 | } 223 | } 224 | } 225 | 226 | #endif 227 | -------------------------------------------------------------------------------- /src/zerolib/System/Console.Windows.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if WINDOWS 18 | 19 | using System.Runtime.InteropServices; 20 | 21 | namespace System 22 | { 23 | public static partial class Console 24 | { 25 | private enum BOOL : int 26 | { 27 | FALSE = 0, 28 | TRUE = 1, 29 | } 30 | 31 | [DllImport("kernel32"), SuppressGCTransition] 32 | private static unsafe extern IntPtr GetStdHandle(int c); 33 | 34 | private readonly static IntPtr s_outputHandle = GetStdHandle(-11); 35 | 36 | private readonly static IntPtr s_inputHandle = GetStdHandle(-10); 37 | 38 | [DllImport("kernel32", EntryPoint = "SetConsoleTitleW"), SuppressGCTransition] 39 | private static unsafe extern BOOL SetConsoleTitle(char* c); 40 | 41 | public static unsafe string Title 42 | { 43 | set 44 | { 45 | fixed (char* c = value) 46 | SetConsoleTitle(c); 47 | } 48 | } 49 | 50 | [StructLayout(LayoutKind.Sequential)] 51 | struct CONSOLE_CURSOR_INFO 52 | { 53 | public uint Size; 54 | public BOOL Visible; 55 | } 56 | 57 | [DllImport("kernel32"), SuppressGCTransition] 58 | private static unsafe extern BOOL SetConsoleCursorInfo(IntPtr handle, CONSOLE_CURSOR_INFO* cursorInfo); 59 | 60 | public static unsafe bool CursorVisible 61 | { 62 | set 63 | { 64 | CONSOLE_CURSOR_INFO cursorInfo = new CONSOLE_CURSOR_INFO 65 | { 66 | Size = 1, 67 | Visible = value ? BOOL.TRUE : BOOL.FALSE 68 | }; 69 | SetConsoleCursorInfo(s_outputHandle, &cursorInfo); 70 | } 71 | } 72 | 73 | [DllImport("kernel32"), SuppressGCTransition] 74 | private static unsafe extern BOOL SetConsoleTextAttribute(IntPtr handle, ushort attribute); 75 | 76 | public static ConsoleColor ForegroundColor 77 | { 78 | set 79 | { 80 | SetConsoleTextAttribute(s_outputHandle, (ushort)value); 81 | } 82 | } 83 | 84 | [StructLayout(LayoutKind.Sequential)] 85 | private struct KEY_EVENT_RECORD 86 | { 87 | public BOOL KeyDown; 88 | public short RepeatCount; 89 | public short VirtualKeyCode; 90 | public short VirtualScanCode; 91 | public short UChar; 92 | public int ControlKeyState; 93 | } 94 | 95 | [StructLayout(LayoutKind.Sequential)] 96 | private struct INPUT_RECORD 97 | { 98 | public short EventType; 99 | public KEY_EVENT_RECORD KeyEvent; 100 | } 101 | 102 | [DllImport("kernel32", EntryPoint = "PeekConsoleInputW", CharSet = CharSet.Unicode), SuppressGCTransition] 103 | private static unsafe extern BOOL PeekConsoleInput(IntPtr hConsoleInput, INPUT_RECORD* lpBuffer, uint nLength, uint* lpNumberOfEventsRead); 104 | 105 | public static unsafe bool KeyAvailable 106 | { 107 | get 108 | { 109 | uint nRead; 110 | INPUT_RECORD buffer; 111 | while (true) 112 | { 113 | PeekConsoleInput(s_inputHandle, &buffer, 1, &nRead); 114 | 115 | if (nRead == 0) 116 | return false; 117 | 118 | if (buffer.EventType == 1 && buffer.KeyEvent.KeyDown != BOOL.FALSE) 119 | return true; 120 | 121 | ReadConsoleInput(s_inputHandle, &buffer, 1, &nRead); 122 | } 123 | } 124 | } 125 | 126 | [DllImport("kernel32", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode), SuppressGCTransition] 127 | private static unsafe extern BOOL ReadConsoleInput(IntPtr hConsoleInput, INPUT_RECORD* lpBuffer, uint nLength, uint* lpNumberOfEventsRead); 128 | 129 | public static unsafe ConsoleKeyInfo ReadKey(bool intercept) 130 | { 131 | uint nRead; 132 | INPUT_RECORD buffer; 133 | do 134 | { 135 | ReadConsoleInput(s_inputHandle, &buffer, 1, &nRead); 136 | } 137 | while (buffer.EventType != 1 || buffer.KeyEvent.KeyDown == BOOL.FALSE); 138 | 139 | return new ConsoleKeyInfo((char)buffer.KeyEvent.UChar, (ConsoleKey)buffer.KeyEvent.VirtualKeyCode, false, false, false); 140 | } 141 | 142 | struct SMALL_RECT 143 | { 144 | public short Left, Top, Right, Bottom; 145 | } 146 | 147 | [DllImport("kernel32"), SuppressGCTransition] 148 | private static unsafe extern BOOL SetConsoleWindowInfo(IntPtr handle, BOOL absolute, SMALL_RECT* consoleWindow); 149 | 150 | public static unsafe void SetWindowSize(int x, int y) 151 | { 152 | SMALL_RECT rect = new SMALL_RECT 153 | { 154 | Left = 0, 155 | Top = 0, 156 | Right = (short)(x - 1), 157 | Bottom = (short)(y - 1), 158 | }; 159 | SetConsoleWindowInfo(s_outputHandle, BOOL.TRUE, &rect); 160 | } 161 | 162 | [StructLayout(LayoutKind.Sequential)] 163 | struct COORD 164 | { 165 | public short X, Y; 166 | } 167 | 168 | [DllImport("kernel32"), SuppressGCTransition] 169 | private static unsafe extern BOOL SetConsoleScreenBufferSize(IntPtr handle, COORD size); 170 | 171 | public static void SetBufferSize(int x, int y) 172 | { 173 | SetConsoleScreenBufferSize(s_outputHandle, new COORD { X = (short)x, Y = (short)y }); 174 | } 175 | 176 | [DllImport("kernel32"), SuppressGCTransition] 177 | private static unsafe extern BOOL SetConsoleCursorPosition(IntPtr handle, COORD position); 178 | 179 | public static void SetCursorPosition(int x, int y) 180 | { 181 | SetConsoleCursorPosition(s_outputHandle, new COORD { X = (short)x, Y = (short)y }); 182 | } 183 | 184 | [DllImport("kernel32", EntryPoint = "WriteConsoleW"), SuppressGCTransition] 185 | private static unsafe extern BOOL WriteConsole(IntPtr handle, void* buffer, int numChars, int* charsWritten, void* reserved); 186 | 187 | public static unsafe void Write(char c) 188 | { 189 | int dummy; 190 | WriteConsole(s_outputHandle, &c, 1, &dummy, null); 191 | } 192 | } 193 | } 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /src/zerolib/System/Console.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public enum ConsoleColor 20 | { 21 | Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, 22 | Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White 23 | } 24 | 25 | public enum ConsoleKey 26 | { 27 | Escape = 27, 28 | LeftArrow = 37, 29 | UpArrow = 38, 30 | RightArrow = 39, 31 | DownArrow = 40, 32 | } 33 | 34 | public readonly struct ConsoleKeyInfo 35 | { 36 | public ConsoleKeyInfo(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) 37 | { 38 | Key = key; 39 | } 40 | 41 | public readonly ConsoleKey Key; 42 | } 43 | 44 | public static unsafe partial class Console 45 | { 46 | public static void WriteLine(string s) 47 | { 48 | for (int i = 0; i < s.Length; i++) 49 | Console.Write(s[i]); 50 | #if WINDOWS || UEFI 51 | Console.Write('\r'); 52 | #endif 53 | Console.Write('\n'); 54 | } 55 | 56 | public static void WriteLine(int i) 57 | { 58 | const int BufferSize = 16; 59 | char* pBuffer = stackalloc char[BufferSize]; 60 | if (i < 0) 61 | { 62 | Write('-'); 63 | } 64 | 65 | char* pEnd = &pBuffer[BufferSize - 1]; 66 | char* pCurrent = pEnd; 67 | do 68 | { 69 | *(pCurrent--) = (char)((i % 10) + '0'); 70 | i /= 10; 71 | } while (i != 0); 72 | 73 | while (pCurrent <= pEnd) 74 | Write(*(pCurrent++)); 75 | 76 | #if WINDOWS || UEFI 77 | Console.Write('\r'); 78 | #endif 79 | Console.Write('\n'); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/zerolib/System/Delegate.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public abstract class Delegate 20 | { 21 | internal object m_firstParameter; 22 | internal object m_helperObject; 23 | internal nint m_extraFunctionPointerOrData; 24 | internal IntPtr m_functionPointer; 25 | 26 | private void InitializeClosedStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) 27 | { 28 | m_extraFunctionPointerOrData = functionPointer; 29 | m_helperObject = firstParameter; 30 | m_functionPointer = functionPointerThunk; 31 | m_firstParameter = this; 32 | } 33 | 34 | private void InitializeOpenStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) 35 | { 36 | m_firstParameter = this; 37 | m_functionPointer = functionPointerThunk; 38 | m_extraFunctionPointerOrData = functionPointer; 39 | } 40 | 41 | private void InitializeClosedInstance(object firstParameter, IntPtr functionPointer) 42 | { 43 | m_functionPointer = functionPointer; 44 | m_firstParameter = firstParameter; 45 | } 46 | } 47 | 48 | public abstract class MulticastDelegate : Delegate { } 49 | } 50 | -------------------------------------------------------------------------------- /src/zerolib/System/Enum.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public abstract class Enum : ValueType { } 20 | } 21 | -------------------------------------------------------------------------------- /src/zerolib/System/Environment.Efi.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if UEFI 18 | 19 | using System.Runtime.InteropServices; 20 | using Internal.Runtime.CompilerHelpers; 21 | 22 | namespace System 23 | { 24 | public static unsafe partial class Environment 25 | { 26 | public unsafe static void FailFast(string message) 27 | { 28 | fixed (char* pMessage = message ?? "FailFast") 29 | { 30 | EfiSystemTable->ConOut->OutputString(EfiSystemTable->ConOut, pMessage); 31 | } 32 | while (true) ; 33 | } 34 | 35 | static internal long s_lastTickCount; 36 | static internal long s_stallSinceLastTickCount; 37 | 38 | public static unsafe long TickCount64 39 | { 40 | get 41 | { 42 | EFI_TIME time; 43 | EfiSystemTable->RuntimeServices->GetTime(&time, null); 44 | long days = time.Year * 365 + time.Month * 31 + time.Day; 45 | long seconds = days * 24 * 60 * 60 + time.Hour * 60 * 60 + time.Minute * 60 + time.Second; 46 | long milliseconds = seconds * 1000 + time.Nanosecond / 1000000; 47 | 48 | // HACK: some systems will report a zero Nanosecond part. We keep track of Stall getting 49 | // previously called with the same tickcount and artificially inflate the tick count 50 | // by the amount of stall if it occured within the same TickCount. 51 | if (s_lastTickCount == milliseconds) 52 | { 53 | milliseconds += s_stallSinceLastTickCount; 54 | } 55 | else 56 | { 57 | s_lastTickCount = milliseconds; 58 | s_stallSinceLastTickCount = 0; 59 | } 60 | return milliseconds; 61 | } 62 | } 63 | } 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/zerolib/System/Environment.Unix.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if LINUX 18 | 19 | using System.Runtime.InteropServices; 20 | 21 | namespace System 22 | { 23 | public static partial class Environment 24 | { 25 | [DllImport("libSystem.Native"), SuppressGCTransition] 26 | public static extern long SystemNative_GetTimestamp(); 27 | 28 | public static long TickCount64 => SystemNative_GetTimestamp() / 1_000_000; 29 | 30 | [DllImport("libSystem.Native"), SuppressGCTransition] 31 | public static extern void SystemNative_Abort(); 32 | 33 | public static void FailFast(string message) 34 | { 35 | SystemNative_Abort(); 36 | } 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/zerolib/System/Environment.Windows.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if WINDOWS 18 | 19 | using System.Runtime.InteropServices; 20 | 21 | namespace System 22 | { 23 | public static partial class Environment 24 | { 25 | [DllImport("kernel32"), SuppressGCTransition] 26 | private static extern long GetTickCount64(); 27 | 28 | public static long TickCount64 => GetTickCount64(); 29 | 30 | [DllImport("kernel32"), SuppressGCTransition] 31 | private static extern void RaiseFailFastException(IntPtr a, IntPtr b, int flags); 32 | 33 | public static void FailFast(string message) 34 | { 35 | RaiseFailFastException(default, default, default); 36 | } 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/zerolib/System/Nullable.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public struct Nullable where T : struct 20 | { 21 | private readonly bool _hasValue; 22 | private T _value; 23 | 24 | public Nullable(T value) => (_hasValue, _value) = (true, value); 25 | 26 | public readonly bool HasValue => _hasValue; 27 | 28 | public readonly T Value 29 | { 30 | get 31 | { 32 | if (!_hasValue) 33 | Environment.FailFast(null); 34 | return _value; 35 | } 36 | } 37 | 38 | public static implicit operator Nullable(T value) => new Nullable(value); 39 | 40 | public static explicit operator T(Nullable value) => value.Value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/zerolib/System/Object.Efi.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if UEFI 18 | 19 | using Internal.Runtime.CompilerHelpers; 20 | 21 | namespace System 22 | { 23 | public unsafe partial class Object 24 | { 25 | private static void* s_efiSystemTable; 26 | 27 | internal static EFI_SYSTEM_TABLE* EfiSystemTable => (EFI_SYSTEM_TABLE*)s_efiSystemTable; 28 | internal static void SetEfiSystemTable(EFI_SYSTEM_TABLE* t) => s_efiSystemTable = t; 29 | } 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/zerolib/System/Object.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime; 18 | 19 | namespace System 20 | { 21 | public partial class Object 22 | { 23 | #pragma warning disable 169 24 | // The layout of object is a contract with the compiler. 25 | internal unsafe MethodTable* m_pMethodTable; 26 | #pragma warning restore 169 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/zerolib/System/Primitives.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public struct Void { } 20 | 21 | // The layout of primitive types is special cased because it would be recursive. 22 | // These really don't need any fields to work. 23 | public struct Boolean { } 24 | public struct Char { } 25 | public struct SByte { } 26 | public struct Byte { } 27 | public struct Int16 { } 28 | public struct UInt16 { } 29 | public struct Int32 { } 30 | public struct UInt32 { } 31 | public struct Int64 { } 32 | public struct UInt64 { } 33 | public struct IntPtr { } 34 | public struct UIntPtr { } 35 | public struct Single { } 36 | public struct Double { } 37 | } 38 | -------------------------------------------------------------------------------- /src/zerolib/System/ReadOnlySpan.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime.CompilerServices; 18 | using System.Runtime.InteropServices; 19 | 20 | namespace System 21 | { 22 | public readonly ref struct ReadOnlySpan 23 | { 24 | private readonly ref T _reference; 25 | private readonly int _length; 26 | 27 | public int Length => _length; 28 | 29 | public ReadOnlySpan(T[] array) 30 | { 31 | if (array == null) 32 | { 33 | this = default; 34 | return; 35 | } 36 | 37 | _reference = ref MemoryMarshal.GetArrayDataReference(array); 38 | _length = array.Length; 39 | } 40 | 41 | public unsafe ReadOnlySpan(void* pointer, int length) 42 | { 43 | _reference = ref Unsafe.As(ref *(byte*)pointer); 44 | _length = length; 45 | } 46 | 47 | public ReadOnlySpan(T[] array, int start, int length) 48 | { 49 | if (array == null) 50 | { 51 | if (start != 0 || length != 0) 52 | Environment.FailFast(null); 53 | this = default; 54 | return; // returns default 55 | } 56 | #if X64 || ARM64 57 | if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length) 58 | Environment.FailFast(null); 59 | #elif X86 || ARM 60 | if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) 61 | Environment.FailFast(null); 62 | #else 63 | #error Nope 64 | #endif 65 | 66 | _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start); 67 | _length = length; 68 | } 69 | 70 | public ref readonly T this[int index] 71 | { 72 | [Intrinsic] 73 | get 74 | { 75 | if ((uint)index >= (uint)_length) 76 | Environment.FailFast(null); 77 | return ref Unsafe.Add(ref _reference, (nint)(uint)index); 78 | } 79 | } 80 | 81 | public static implicit operator ReadOnlySpan(T[] array) => new ReadOnlySpan(array); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/zerolib/System/Reflection/ReflectionAttributes.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System.Reflection 18 | { 19 | public sealed class DefaultMemberAttribute : Attribute 20 | { 21 | public DefaultMemberAttribute(string memberName) { } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/CompilerServices/ClassConstructorRunner.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime.InteropServices; 18 | 19 | namespace System.Runtime.CompilerServices 20 | { 21 | // A class responsible for running static constructors. The compiler will call into this 22 | // code to ensure static constructors run and that they only run once. 23 | internal static partial class ClassConstructorRunner 24 | { 25 | private static IntPtr CheckStaticClassConstructionReturnNonGCStaticBase(ref StaticClassConstructionContext context, IntPtr nonGcStaticBase) 26 | { 27 | CheckStaticClassConstruction(ref context); 28 | return nonGcStaticBase; 29 | } 30 | 31 | private static unsafe void CheckStaticClassConstruction(ref StaticClassConstructionContext context) 32 | { 33 | // Not dealing with multithreading issues. 34 | if (context.cctorMethodAddress != default) 35 | { 36 | IntPtr address = context.cctorMethodAddress; 37 | context.cctorMethodAddress = default; 38 | ((delegate*)address)(); 39 | } 40 | } 41 | } 42 | 43 | // This data structure is a contract with the compiler. It holds the address of a static 44 | // constructor and a flag that specifies whether the constructor already executed. 45 | [StructLayout(LayoutKind.Sequential)] 46 | public struct StaticClassConstructionContext 47 | { 48 | public IntPtr cctorMethodAddress; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/CompilerServices/CompilerAttributes.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System.Runtime.CompilerServices 18 | { 19 | internal sealed class IntrinsicAttribute : Attribute { } 20 | 21 | public enum MethodImplOptions 22 | { 23 | Unmanaged = 0x0004, 24 | NoInlining = 0x0008, 25 | ForwardRef = 0x0010, 26 | Synchronized = 0x0020, 27 | NoOptimization = 0x0040, 28 | PreserveSig = 0x0080, 29 | AggressiveInlining = 0x0100, 30 | AggressiveOptimization = 0x0200, 31 | InternalCall = 0x1000 32 | } 33 | 34 | public sealed class MethodImplAttribute : Attribute 35 | { 36 | public MethodImplAttribute(MethodImplOptions methodImplOptions) { } 37 | } 38 | 39 | public sealed class IndexerNameAttribute: Attribute 40 | { 41 | public IndexerNameAttribute(string indexerName) { } 42 | } 43 | 44 | public class CallConvCdecl { } 45 | public class CallConvFastcall { } 46 | public class CallConvStdcall { } 47 | public class CallConvSuppressGCTransition { } 48 | public class CallConvThiscall { } 49 | public class CallConvMemberFunction { } 50 | 51 | public sealed class InlineArrayAttribute : Attribute 52 | { 53 | public InlineArrayAttribute(int size) { } 54 | } 55 | 56 | public sealed class ExtensionAttribute : Attribute { } 57 | } 58 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/CompilerServices/RuntimeFeature.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System.Runtime.CompilerServices 18 | { 19 | public static class RuntimeFeature 20 | { 21 | public const string PortablePdb = nameof(PortablePdb); 22 | public const string DefaultImplementationsOfInterfaces = nameof(DefaultImplementationsOfInterfaces); 23 | public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention); 24 | public const string CovariantReturnsOfClasses = nameof(CovariantReturnsOfClasses); 25 | public const string ByRefFields = nameof(ByRefFields); 26 | public const string VirtualStaticsInInterfaces = nameof(VirtualStaticsInInterfaces); 27 | public const string NumericIntPtr = nameof(NumericIntPtr); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/CompilerServices/RuntimeHelpers.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime.InteropServices; 18 | 19 | namespace System.Runtime.CompilerServices 20 | { 21 | public class RuntimeHelpers 22 | { 23 | public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int); 24 | } 25 | 26 | [StructLayout(LayoutKind.Sequential)] 27 | internal class RawArrayData 28 | { 29 | public uint Length; 30 | #if X64 || ARM64 31 | public uint Padding; 32 | #elif X86 || ARM 33 | // No padding on 32bit 34 | #else 35 | #error Nope 36 | #endif 37 | public byte Data; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/CompilerServices/Unsafe.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System.Runtime.CompilerServices 18 | { 19 | public static unsafe partial class Unsafe 20 | { 21 | // The body of this method is generated by the compiler. 22 | // It will do what Unsafe.Add is expected to do. It's just not possible to express it in C#. 23 | [Intrinsic] 24 | public static extern ref T Add(ref T source, int elementOffset); 25 | [Intrinsic] 26 | public static extern ref T Add(ref T source, IntPtr elementOffset); 27 | [Intrinsic] 28 | public static extern ref TTo As(ref TFrom source); 29 | [Intrinsic] 30 | public static extern T As(object o) where T : class; 31 | [Intrinsic] 32 | public static extern void* AsPointer(ref T value); 33 | [Intrinsic] 34 | public static extern ref T AsRef(void* source); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/InteropServices/InteropAttributes.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System.Runtime.InteropServices 18 | { 19 | public enum UnmanagedType { } 20 | 21 | public enum CharSet 22 | { 23 | None = 1, 24 | Ansi = 2, 25 | Unicode = 3, 26 | Auto = 4, 27 | } 28 | 29 | public enum CallingConvention 30 | { 31 | Winapi = 1, 32 | Cdecl = 2, 33 | StdCall = 3, 34 | ThisCall = 4, 35 | FastCall = 5, 36 | } 37 | 38 | public sealed class DllImportAttribute : Attribute 39 | { 40 | public string EntryPoint; 41 | public CharSet CharSet; 42 | public bool ExactSpelling; 43 | public CallingConvention CallingConvention; 44 | public DllImportAttribute(string dllName) { } 45 | } 46 | 47 | public sealed class UnmanagedCallersOnlyAttribute : Attribute 48 | { 49 | public string EntryPoint; 50 | public UnmanagedCallersOnlyAttribute() { } 51 | } 52 | 53 | public enum LayoutKind 54 | { 55 | Sequential = 0, 56 | Explicit = 2, 57 | Auto = 3, 58 | } 59 | 60 | public sealed class StructLayoutAttribute : Attribute 61 | { 62 | public int Size; 63 | public int Pack; 64 | public StructLayoutAttribute(LayoutKind layoutKind) { } 65 | } 66 | 67 | public sealed class InAttribute : Attribute 68 | { 69 | } 70 | 71 | public sealed class OutAttribute : Attribute 72 | { 73 | } 74 | 75 | public sealed class SuppressGCTransitionAttribute : Attribute 76 | { 77 | public SuppressGCTransitionAttribute() { } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/zerolib/System/Runtime/InteropServices/MemoryMarshal.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime.CompilerServices; 18 | 19 | namespace System.Runtime.InteropServices 20 | { 21 | public static partial class MemoryMarshal 22 | { 23 | [Intrinsic] 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static ref T GetArrayDataReference(T[] array) => ref Unsafe.As(ref Unsafe.As(array).Data); 26 | 27 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 28 | public unsafe static System.Span CreateSpan(ref T reference, int length) 29 | => new System.Span(Unsafe.AsPointer(ref reference), length); 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public unsafe static System.ReadOnlySpan CreateReadOnlySpan(ref T reference, int length) 33 | => new System.ReadOnlySpan(Unsafe.AsPointer(ref reference), length); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/zerolib/System/RuntimeHandles.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public struct RuntimeTypeHandle 20 | { 21 | } 22 | 23 | public struct RuntimeMethodHandle 24 | { 25 | } 26 | 27 | public struct RuntimeFieldHandle 28 | { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/zerolib/System/Span.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime.CompilerServices; 18 | using System.Runtime.InteropServices; 19 | 20 | namespace System 21 | { 22 | public readonly ref struct Span 23 | { 24 | private readonly ref T _reference; 25 | private readonly int _length; 26 | 27 | public int Length => _length; 28 | 29 | public Span(T[] array) 30 | { 31 | if (array == null) 32 | { 33 | this = default; 34 | return; 35 | } 36 | 37 | _reference = ref MemoryMarshal.GetArrayDataReference(array); 38 | _length = array.Length; 39 | } 40 | 41 | public unsafe Span(void* pointer, int length) 42 | { 43 | _reference = ref Unsafe.As(ref *(byte*)pointer); 44 | _length = length; 45 | } 46 | 47 | public Span(T[] array, int start, int length) 48 | { 49 | if (array == null) 50 | { 51 | if (start != 0 || length != 0) 52 | Environment.FailFast(null); 53 | this = default; 54 | return; // returns default 55 | } 56 | #if X64 || ARM64 57 | if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length) 58 | Environment.FailFast(null); 59 | #elif X86 || ARM 60 | if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) 61 | Environment.FailFast(null); 62 | #else 63 | #error Nope 64 | #endif 65 | 66 | _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start); 67 | _length = length; 68 | } 69 | 70 | public ref T this[int index] 71 | { 72 | [Intrinsic] 73 | get 74 | { 75 | if ((uint)index >= (uint)_length) 76 | Environment.FailFast(null); 77 | return ref Unsafe.Add(ref _reference, (nint)(uint)index); 78 | } 79 | } 80 | 81 | public unsafe void Clear() 82 | { 83 | for (int i = 0; i < _length; i++) 84 | Unsafe.Add(ref _reference, i) = default; 85 | } 86 | 87 | public unsafe void Fill(T value) 88 | { 89 | for (int i = 0; i < _length; i++) 90 | Unsafe.Add(ref _reference, i) = value; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/zerolib/System/String.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | using System.Runtime; 18 | using System.Runtime.CompilerServices; 19 | 20 | namespace System 21 | { 22 | public sealed class String 23 | { 24 | // The layout of the string type is a contract with the compiler. 25 | private readonly int _length; 26 | private char _firstChar; 27 | 28 | public int Length => _length; 29 | 30 | [IndexerName("Chars")] 31 | public unsafe char this[int index] 32 | { 33 | [System.Runtime.CompilerServices.Intrinsic] 34 | get 35 | { 36 | return System.Runtime.CompilerServices.Unsafe.Add(ref _firstChar, index); 37 | } 38 | } 39 | 40 | [MethodImpl(MethodImplOptions.InternalCall)] 41 | public extern unsafe String(char* value); 42 | 43 | private static unsafe string Ctor(char* ptr) 44 | { 45 | char* cur = ptr; 46 | while (*cur++ != 0) ; 47 | 48 | string result = FastNewString((int)(cur - ptr - 1)); 49 | for (int i = 0; i < cur - ptr - 1; i++) 50 | Unsafe.Add(ref result._firstChar, i) = ptr[i]; 51 | return result; 52 | } 53 | 54 | [MethodImpl(MethodImplOptions.InternalCall)] 55 | public extern unsafe String(sbyte* value); 56 | 57 | private static unsafe string Ctor(sbyte* ptr) 58 | { 59 | sbyte* cur = ptr; 60 | while (*cur++ != 0) ; 61 | 62 | string result = FastNewString((int)(cur - ptr - 1)); 63 | for (int i = 0; i < cur - ptr - 1; i++) 64 | { 65 | if (ptr[i] > 0x7F) 66 | Environment.FailFast(null); 67 | Unsafe.Add(ref result._firstChar, i) = (char)ptr[i]; 68 | } 69 | return result; 70 | } 71 | 72 | static unsafe string FastNewString(int numChars) 73 | { 74 | return NewString("".m_pMethodTable, numChars); 75 | 76 | [MethodImpl(MethodImplOptions.InternalCall)] 77 | [RuntimeImport("*", "RhpNewArray")] 78 | static extern string NewString(MethodTable* pMT, int numElements); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/zerolib/System/Thread.Efi.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if UEFI 18 | 19 | namespace System.Threading 20 | { 21 | public static class Thread 22 | { 23 | public static unsafe void Sleep(int delayMs) 24 | { 25 | EfiSystemTable->BootServices->Stall(1000 * (uint)delayMs); 26 | Environment.s_stallSinceLastTickCount += delayMs; 27 | } 28 | } 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/zerolib/System/Thread.Unix.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if LINUX 18 | 19 | using System.Runtime.InteropServices; 20 | 21 | namespace System.Threading 22 | { 23 | public static class Thread 24 | { 25 | [DllImport("libSystem.Native"), SuppressGCTransition] 26 | private static extern IntPtr SystemNative_LowLevelMonitor_Create(); 27 | 28 | [DllImport("libSystem.Native"), SuppressGCTransition] 29 | private static extern void SystemNative_LowLevelMonitor_TimedWait(IntPtr mon, int ms); 30 | 31 | private static IntPtr s_dummyMonitor = SystemNative_LowLevelMonitor_Create(); 32 | 33 | public static void Sleep(int delayMs) => SystemNative_LowLevelMonitor_TimedWait(s_dummyMonitor, delayMs); 34 | } 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/zerolib/System/Thread.Windows.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | #if WINDOWS 18 | 19 | using System.Runtime.InteropServices; 20 | 21 | namespace System.Threading 22 | { 23 | public static class Thread 24 | { 25 | [DllImport("kernel32"), SuppressGCTransition] 26 | public static extern void Sleep(int delayMs); 27 | } 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/zerolib/System/Type.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public class Type { } 20 | public class RuntimeType : Type { } 21 | } 22 | -------------------------------------------------------------------------------- /src/zerolib/System/ValueTuple.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public struct ValueTuple 20 | { 21 | public T1 Item1; 22 | public T2 Item2; 23 | 24 | public ValueTuple(T1 t1, T2 t2) 25 | { 26 | Item1 = t1; 27 | Item2 = t2; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/zerolib/System/ValueType.cs: -------------------------------------------------------------------------------- 1 | // bflat minimal runtime library 2 | // Copyright (C) 2021-2022 Michal Strehovsky 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published 6 | // by the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | namespace System 18 | { 19 | public abstract class ValueType { } 20 | } 21 | --------------------------------------------------------------------------------