├── .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 |
--------------------------------------------------------------------------------