diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 00000000..895e0bf3
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,20 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "docfx": {
+ "version": "2.76.0",
+ "commands": [
+ "docfx"
+ ],
+ "rollForward": false
+ },
+ "docfxtocgenerator": {
+ "version": "1.19.0",
+ "commands": [
+ "DocFxTocGenerator"
+ ],
+ "rollForward": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index 32e510c8..54d97477 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,6 +4,9 @@ root = true
end_of_line = lf
indent_style = space
+[*.yml]
+indent_size = 2
+
[*.{csproj,ilproj,props,targets}]
indent_size = 2
diff --git a/.github/.github.csproj b/.github/.github.csproj
new file mode 100644
index 00000000..4d7b3159
--- /dev/null
+++ b/.github/.github.csproj
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1
new file mode 100644
index 00000000..5509425b
--- /dev/null
+++ b/.github/gen-test-matrix.ps1
@@ -0,0 +1,216 @@
+param (
+ [string[]] $MatrixOutName,
+ [string] $GithubOutput
+)
+
+$ErrorActionPreference = "Stop";
+
+$operatingSystems = @(
+ [pscustomobject]@{
+ name = "Windows";
+ runner = "windows-latest";
+ ridname = "win";
+ arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners
+ runnerArch = 1;
+ hasFramework = $true;
+ monoArch = @("win32", "win64", "win_arm64");
+ monoDll = "mono-2.0-bdwgc.dll";
+ },
+ [pscustomobject]@{
+ name = "Linux";
+ runner = "ubuntu-latest";
+ ridname = "linux";
+ arch = @("x64");
+ runnerArch = 0;
+ hasMono = $true;
+ monoArch = @("linux64");
+ monoDll = "limonobdwgc-2.0.so"; # TODO
+ },
+ [pscustomobject]@{
+ name = "MacOS 13";
+ runner = "macos-13";
+ ridname = "osx";
+ arch = @("x64");
+ runnerArch = 0;
+ hasMono = $true;
+ monoArch = @("macos_x64");
+ monoDll = "limonobdwgc-2.0.dylib";
+ },
+ [pscustomobject]@{
+ #enable = $false;
+ name = "MacOS 14 (M1)";
+ runner = "macos-14";
+ ridname = "osx";
+ arch = @("x64"<#, "arm64"#>); # x64 comes from Rosetta, and we disable arm64 mode for now because we don't support it yet
+ runnerArch = 1;
+ hasMono = $true;
+ monoArch = @("macos_x64", "macos_arm64");
+ monoDll = "limonobdwgc-2.0.dylib";
+ }
+);
+
+$dotnetVersions = @(
+ [pscustomobject]@{
+ name = ".NET Framework 4.x";
+ id = 'fx';
+ tfm = "net462";
+ rids = @("win-x86","win-x64","win-arm64");
+ isFramework = $true;
+ },
+ [pscustomobject]@{
+ name = ".NET Core 2.1";
+ sdk = "2.1";
+ tfm = "netcoreapp2.1";
+ rids = @("win-x86","win-x64","linux-x64","osx-x64");
+ needsRestore = $true;
+ },
+ [pscustomobject]@{
+ name = ".NET Core 3.0";
+ sdk = "3.0";
+ tfm = "netcoreapp3.0";
+ rids = @("win-x86","win-x64","linux-x64","osx-x64");
+ },
+ [pscustomobject]@{
+ name = ".NET Core 3.1";
+ sdk = "3.1";
+ tfm = "netcoreapp3.1";
+ rids = @("win-x86","win-x64","linux-x64","osx-x64");
+ },
+ [pscustomobject]@{
+ name = ".NET 5.0";
+ sdk = "5.0";
+ tfm = "net5.0";
+ rids = @("win-x86","win-x64","linux-x64","osx-x64");
+ },
+ [pscustomobject]@{
+ name = ".NET 6.0";
+ sdk = "6.0";
+ tfm = "net6.0";
+ rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64");
+ pgo = $true;
+ },
+ [pscustomobject]@{
+ name = ".NET 7.0";
+ sdk = "7.0";
+ tfm = "net7.0";
+ rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64");
+ pgo = $true;
+ },
+ [pscustomobject]@{
+ name = ".NET 8.0";
+ sdk = "8.0";
+ tfm = "net8.0";
+ rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64");
+ pgo = $true;
+ }
+);
+
+$monoTfm = "net462";
+
+$monoVersions = @(
+ <#
+ [pscustomobject]@{
+ name = "Unity Mono 6000.0.2";
+ unityVersion = "6000.0.2";
+ monoName = "MonoBleedingEdge";
+ }
+ #>
+);
+
+$jobs = @();
+
+foreach ($os in $operatingSystems)
+{
+ if ($os.enable -eq $false) { continue; }
+ $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll,runnerArch
+
+ if ($os.hasMono -and $os.runnerArch -lt $os.arch.Length)
+ {
+ # this OS has a system mono, emit a job for that
+ $jobs += @(
+ [pscustomobject]@{
+ title = "System Mono on $($os.name)";
+ os = $outos;
+ dotnet = [pscustomobject]@{
+ name = "Mono";
+ id = "sysmono";
+ needsRestore = $true; # Monos always need restore
+ isMono = $true;
+ systemMono = $true;
+ tfm = $monoTfm;
+ };
+ arch = $os.arch[$os.runnerArch];
+ }
+ );
+ }
+
+ foreach ($arch in $os.arch)
+ {
+ $rid = $os.ridname + "-" + $arch;
+
+ foreach ($dotnet in $dotnetVersions)
+ {
+ if ($dotnet.enable -eq $false) { continue; }
+
+ if ($dotnet.isFramework -and -not $os.hasFramework)
+ {
+ # we're looking at .NET Framework, but this OS doesn't support it
+ continue;
+ }
+
+ if (-not $dotnet.rids -contains $rid)
+ {
+ # the current OS/arch/runtime triple is not supported by .NET, skip
+ continue;
+ }
+
+ $outdotnet = $dotnet | Select-Object -ExcludeProperty rids
+
+ $title = "$($dotnet.name) $arch on $($os.name)"
+ if ($dotnet.pgo)
+ {
+ # this runtime supports pgo; generate 2 jobs; one with it enabled, one without
+ $jobs += @(
+ [pscustomobject]@{
+ title = $title + " (PGO Off)";
+ os = $outos;
+ dotnet = $outdotnet;
+ arch = $arch;
+ usePgo = $false;
+ },
+ [pscustomobject]@{
+ title = $title + " (PGO On)";
+ os = $outos;
+ dotnet = $outdotnet;
+ arch = $arch;
+ usePgo = $true;
+ }
+ );
+ }
+ else
+ {
+ # this is a normal job; only add one
+ $jobs += @(
+ [pscustomobject]@{
+ title = $title;
+ os = $outos;
+ dotnet = $outdotnet;
+ arch = $arch;
+ }
+ );
+ }
+ }
+
+ # TODO: non-system mono
+ }
+}
+
+# TODO: support multiple batches
+if ($jobs.Length -gt 256)
+{
+ Write-Error "Generated more than 256 jobs; actions will fail!";
+}
+
+$matrixObj = [pscustomobject]@{include = $jobs;};
+$matrixStr = ConvertTo-Json -Compress -Depth 5 $matrixObj;
+echo "$($MatrixOutName[0])=$matrixStr" >> $GithubOutput;
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..11bc83c8
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,123 @@
+name: Build
+
+on:
+ workflow_call:
+ inputs:
+ os:
+ required: true
+ type: string
+ osname:
+ required: true
+ type: string
+ version:
+ required: true
+ type: string
+ no-suffix:
+ type: boolean
+ default: false
+ upload-packages:
+ type: boolean
+ default: false
+ upload-tests:
+ type: boolean
+ default: false
+
+defaults:
+ run:
+ shell: pwsh
+
+env:
+ DOTNET_TELEMETRY_OPTOUT: true
+ DOTNET_NOLOGO: true
+ NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg
+
+jobs:
+ build:
+ runs-on: ${{ inputs.os }}
+ name: Build
+ env:
+ LOG_FILE_NAME: testresults.${{ inputs.os }}.auxtests.trx
+ VersionSuffix: ${{ !inputs.no-suffix && format('daily.{0}', inputs.version) }}
+ DoNotAddSuffix: ${{ inputs.no-suffix && '1' }}
+ steps:
+ - name: Configure git
+ run: |
+ git config --global core.autocrlf input
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: true
+ submodules: recursive
+
+ # TODO: maybe we can eventually use package locks for package caching?
+
+ - name: Install .NET SDK
+ uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47
+ with:
+ global-json: global.json
+
+ # NOTE: manual package caching
+ - name: Cache restored NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ${{ env.NUGET_PACKAGES }}
+ key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}
+ restore-keys: ${{ runner.os }}-nuget-v1-
+
+ - name: Restore
+ run: dotnet restore -bl:restore.binlog -noAutoRsp
+
+ - name: Build
+ run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:build.binlog -clp:NoSummary -noAutoRsp
+
+ - name: Pack
+ run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp
+
+ # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds
+ # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be.
+
+ # TODO: If/when we add other test projects aside from MonoMod.UnitTest, we should run tests here
+ #- name: Run auxiliary tests
+ # run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" --filter "FullyQualifiedName!~MonoMod.UnitTest"
+ #
+ #- name: Upload test results
+ # uses: actions/upload-artifact@v4
+ # if: ${{ always() }}
+ # with:
+ # name: test-results aux ${{ runner.os }}
+ # retention-days: 1
+ # path: 'TestResults/*.trx'
+ # if-no-files-found: ignore
+
+ - name: Upload binlogs
+ uses: actions/upload-artifact@v4
+ if: ${{ always() }}
+ with:
+ name: binlogs-${{ runner.os }}
+ path: '*.binlog'
+ retention-days: 7
+
+ - name: Archive packages
+ uses: actions/upload-artifact@v4
+ if: ${{ inputs.upload-packages }}
+ with:
+ name: packages
+ path: artifacts/package/release/*.nupkg
+
+ - name: Archive packages
+ uses: actions/upload-artifact@v4
+ if: ${{ !inputs.upload-packages }}
+ with:
+ name: packages ${{ runner.os }}
+ path: artifacts/package/release/*.nupkg
+ retention-days: 7
+
+ - name: Upload test assets
+ uses: actions/upload-artifact@v4
+ if: ${{ inputs.upload-tests }}
+ with:
+ name: test-assets
+ retention-days: 1
+ path: |
+ artifacts/bin/MonoMod.UnitTest/*/**/*
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..bd12fe91
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,94 @@
+name: Build and Test
+on:
+ push:
+ pull_request:
+
+defaults:
+ run:
+ shell: pwsh
+
+env:
+ DOTNET_TELEMETRY_OPTOUT: true
+ DOTNET_NOLOGO: true
+ NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg
+
+# We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds)
+# Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run
+# a load of tests. This will (eventually) include ARM runners, where possible.
+jobs:
+ event_file:
+ # This job uploads an event file so that our test aggregator and recorder can understand this event
+ name: "Event File"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Upload
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-event-file
+ path: ${{ github.event_path }}
+ retention-days: 1
+
+ compute-version:
+ name: Compute Version
+ runs-on: ubuntu-latest
+ outputs:
+ ver: ${{ steps.computever.outputs.ver }}
+ steps:
+ - id: computever
+ run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT
+
+ compute-test-matrix:
+ name: Compute Test Matrix
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.compute-matrix.outputs.matrix }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: false
+ submodules: false
+ - name: Compute test matrix
+ id: compute-matrix
+ run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT
+
+ build-testassets:
+ needs: compute-version
+ name: 'Build #${{ needs.compute-version.outputs.ver }} (Linux)'
+ uses: ./.github/workflows/build.yml
+ with:
+ os: ubuntu-latest
+ osname: Linux
+ version: ${{ needs.compute-version.outputs.ver }}
+ upload-packages: true
+ upload-tests: true
+
+ build:
+ needs: compute-version
+ strategy:
+ matrix:
+ os: [windows-latest, macos-13]
+ include:
+ - os: windows-latest
+ name: Windows
+ - os: macos-13
+ name: MacOS
+
+ name: 'Build #${{ needs.compute-version.outputs.ver }} (${{ matrix.name }})'
+ uses: ./.github/workflows/build.yml
+ with:
+ os: ${{ matrix.os }}
+ osname: ${{ matrix.name }}
+ version: ${{ needs.compute-version.outputs.ver }}
+ upload-packages: false
+ upload-tests: false
+
+ test:
+ needs: [compute-test-matrix, build-testassets]
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }}
+ uses: ./.github/workflows/test.yml
+ name: Test ${{ matrix.title }}
+ with:
+ matrix: ${{ toJSON(matrix) }}
\ No newline at end of file
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000..8a344835
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,86 @@
+name: Docs
+on:
+ push:
+ branches:
+ - reorganize
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ actions: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+defaults:
+ run:
+ shell: pwsh
+
+env:
+ DOTNET_TELEMETRY_OPTOUT: true
+ DOTNET_NOLOGO: true
+ NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg
+
+jobs:
+ publish-docs:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Configure git
+ run: |
+ git config --global core.autocrlf input
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: true
+ submodules: recursive
+
+ - name: Install .NET SDK
+ uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47
+ with:
+ global-json: global.json
+
+ # NOTE: manual package caching
+ - name: Cache restored NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ${{ env.NUGET_PACKAGES }}
+ key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}
+ restore-keys: ${{ runner.os }}-nuget-v1-
+
+ - name: Restore
+ run: dotnet restore -bl:restore.binlog -noAutoRsp
+
+ - name: Restore .NET tools
+ run: dotnet tool restore
+
+ - name: Build all projects
+ run: dotnet build -c Release -noAutoRsp -bl:build.binlog -p:RunAnalyzers=false # don't run any analyzers to speed up this build
+
+ - name: Build DocFX project
+ run: dotnet docfx docfx/docfx.json
+
+ - name: Upload binlogs
+ uses: actions/upload-artifact@v4
+ if: ${{ always() }}
+ with:
+ name: binlogs
+ path: '*.binlog'
+ retention-days: 7
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ # Upload entire repository
+ path: 'docfx/_site'
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..6eabdf53
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,118 @@
+name: Test
+
+on:
+ workflow_call:
+ inputs:
+ matrix:
+ required: true
+ type: string
+
+defaults:
+ run:
+ shell: pwsh
+
+env:
+ DOTNET_TELEMETRY_OPTOUT: true
+ DOTNET_NOLOGO: true
+ NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg
+ XunitVersion: "2.4.2"
+
+jobs:
+ test:
+ name: Test
+ runs-on: ${{ fromJSON(inputs.matrix).os.runner }}
+ env:
+ LOG_FILE_NAME: testresults.${{ fromJSON(inputs.matrix).os.runner }}.${{ fromJSON(inputs.matrix).dotnet.id != '' && fromJSON(inputs.matrix).dotnet.id || fromJSON(inputs.matrix).dotnet.sdk }}.${{ fromJSON(inputs.matrix).arch }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }}
+ with:
+ lfs: true
+ submodules: recursive
+
+ # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target.
+ - name: Install global SDK
+ if: ${{ ! fromJSON(inputs.matrix).dotnet.needsRestore }}
+ uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47
+ with:
+ architecture: ${{ fromJSON(inputs.matrix).arch }}
+ version: "8.0"
+
+ - name: Cache restored NuGet packages
+ uses: actions/cache@v4
+ if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }}
+ with:
+ path: ${{ env.NUGET_PACKAGES }}
+ key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}
+ restore-keys: ${{ runner.os }}-nuget-v1-
+
+ - name: Install restore SDK
+ if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }}
+ uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47
+ with:
+ architecture: ${{ fromJSON(inputs.matrix).arch }}
+ global-json: global.json
+
+ - name: Restore packages
+ if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }}
+ run: dotnet restore -noAutoRsp
+
+ - name: Download test assets
+ uses: actions/download-artifact@v4
+ with:
+ name: test-assets
+
+ - name: Install test target runtime
+ if: ${{ fromJSON(inputs.matrix).dotnet.sdk != '' }}
+ uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47
+ with:
+ version: ${{ fromJSON(inputs.matrix).dotnet.sdk }}
+ architecture: ${{ fromJSON(inputs.matrix).arch }}
+ runtime: dotnet
+
+ - name: Print SDK info
+ run: dotnet --info
+
+ - name: Fix runtimeconfig.json probing paths
+ if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }}
+ run: |
+ ConvertTo-Json @{runtimeOptions=@{additionalProbingPaths=@($env:NUGET_PACKAGES)}} > release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.runtimeconfig.dev.json
+
+ - name: Run tests
+ if: ${{ ! fromJSON(inputs.matrix).dotnet.isMono && ! fromJSON(inputs.matrix).dotnet.pgo }}
+ run: |
+ dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} `
+ -l:"trx;LogFileName=$($env:LOG_FILE_NAME).trx" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll
+
+ - name: Run tests (PGO)
+ if: ${{ fromJSON(inputs.matrix).dotnet.pgo }}
+ env:
+ DOTNET_ReadyToRun: ${{ !fromJSON(inputs.matrix).usePgo && 1 || 0 }}
+ DOTNET_TC_QuicJitForLoops: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }}
+ DOTNET_TieredPGO: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }}
+ run: |
+ dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} `
+ -l:"trx;LogFileName=$($env:LOG_FILE_NAME).trx" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll
+
+ - name: Run tests (Mono)
+ if: ${{ fromJSON(inputs.matrix).dotnet.isMono && fromJSON(inputs.matrix).dotnet.systemMono }}
+ env:
+ TFM: ${{ fromJSON(inputs.matrix).dotnet.tfm }}
+ run: |
+ mono "$($env:NUGET_PACKAGES)/xunit.runner.console/$($env:XunitVersion)/tools/$($env:TFM)/xunit.console.exe" `
+ "release_$($env:TFM)/MonoMod.UnitTest.dll" -junit "$($env:LOG_FILE_NAME).xml"
+
+ # TODO: Non-system Mono
+
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: ${{ always() }}
+ with:
+ name: test-results ${{ fromJSON(inputs.matrix).title }}
+ retention-days: 1
+ path: |
+ TestResults/*.trx
+ *.xml
+ diaglog.*
diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml
new file mode 100644
index 00000000..c19958db
--- /dev/null
+++ b/.github/workflows/test_results.yml
@@ -0,0 +1,63 @@
+name: Publish Test Results
+
+on:
+ workflow_run:
+ workflows:
+ - Build and Test
+ types: [completed]
+permissions: {}
+
+jobs:
+ publish-test-results:
+ if: github.event.workflow_run.conclusion != 'skipped' && github.event.workflow_run.conclusion != 'cancelled'
+ runs-on: ubuntu-latest
+ name: Publish test results
+
+ permissions:
+ checks: write
+ pull-requests: write
+ contents: read
+ issues: read
+ actions: read
+
+ steps:
+ - name: Download test results
+ uses: actions/download-artifact@v4
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ github.token }}
+ name: test-event-file
+ merge-multiple: false
+
+ - name: Download test results
+ uses: actions/download-artifact@v4
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ github.token }}
+ pattern: test-results *
+ merge-multiple: false
+
+ #- name: Publish Test Results
+ # uses: EnricoMi/publish-unit-test-result-action@v2
+ # with:
+ # commit: ${{ github.event.workflow_run.head_sha }}
+ # event_file: event.json
+ # event_name: ${{ github.event.workflow_run.event }}
+ #
+ # files: '**/*.trx'
+ # comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }}
+ # report_individual_runs: true
+ # compare_to_earlier_commit: false
+
+ - name: Publish Test Results
+ uses: nike4613/actions-test-results@v2
+ with:
+ commit: ${{ github.event.workflow_run.head_sha }}
+ event_file: event.json
+ event_name: ${{ github.event.workflow_run.event }}
+
+ files: |
+ **/*.trx
+ **/*.xml
+ comment_mode: failures
+ comment_on_commit: true
diff --git a/.gitignore b/.gitignore
index e565920f..8890c436 100644
--- a/.gitignore
+++ b/.gitignore
@@ -177,7 +177,6 @@ ClientBin/
*.publishsettings
node_modules/
orleans.codegen.cs
-docfx/
# RIA/Silverlight projects
Generated_Code/
diff --git a/MonoMod.sln b/MonoMod.sln
index 244d1c33..d7b48365 100644
--- a/MonoMod.sln
+++ b/MonoMod.sln
@@ -23,11 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
- azure-pipelines-coretest.yml = azure-pipelines-coretest.yml
- azure-pipelines-docfx.ps1 = azure-pipelines-docfx.ps1
- azure-pipelines-postbuild.yml = azure-pipelines-postbuild.yml
- azure-pipelines-prepushcommon.ps1 = azure-pipelines-prepushcommon.ps1
- azure-pipelines.yml = azure-pipelines.yml
Directory.Build.rsp = Directory.Build.rsp
global.json = global.json
icon.png = icon.png
@@ -52,6 +47,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iced", "external\iced.cspro
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Backports.Tasks", "src\MonoMod.Backports.Tasks\MonoMod.Backports.Tasks.csproj", "{ED959596-2FAE-4B3D-8DF8-8F0058E8068D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = ".github", ".github\.github.csproj", "{179EC228-CED4-429E-934F-422C96273F74}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docs", "docs\docs.csproj", "{4A65448D-466F-4E87-9797-41F43787EFF3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docfx", "docfx\docfx.csproj", "{0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -188,6 +189,30 @@ Global
{ED959596-2FAE-4B3D-8DF8-8F0058E8068D}.Release|Any CPU.Build.0 = Release|Any CPU
{ED959596-2FAE-4B3D-8DF8-8F0058E8068D}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU
{ED959596-2FAE-4B3D-8DF8-8F0058E8068D}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU
+ {179EC228-CED4-429E-934F-422C96273F74}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU
+ {4A65448D-466F-4E87-9797-41F43787EFF3}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -195,6 +220,9 @@ Global
GlobalSection(NestedProjects) = preSolution
{7B682BC4-241C-4B74-8EDC-2D7677A79337} = {BCABFBF3-BB48-4C21-9A52-E140015570A9}
{119A01EE-2238-423C-BE5B-48C68D7D14A7} = {69977055-8E22-4EB0-850D-6EE7D592BC04}
+ {179EC228-CED4-429E-934F-422C96273F74} = {BCABFBF3-BB48-4C21-9A52-E140015570A9}
+ {4A65448D-466F-4E87-9797-41F43787EFF3} = {BCABFBF3-BB48-4C21-9A52-E140015570A9}
+ {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698} = {BCABFBF3-BB48-4C21-9A52-E140015570A9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E10A3D07-6D9A-4898-B95F-268636312A67}
diff --git a/README.md b/README.md
index a2f96376..56c8264b 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ General purpose .NET assembly modding "basework", powered by [cecil](https://git
*MIT-licensed.*
-[![Build status](https://img.shields.io/azure-devops/build/MonoMod/MonoMod/1.svg?style=flat-square)](https://dev.azure.com/MonoMod/MonoMod/_build/latest?definitionId=1) ![Deployment status](https://img.shields.io/azure-devops/release/MonoMod/572c97eb-dbaa-4a55-90e5-1d05431535bd/1/1.svg?style=flat-square)
+[![Build and Test](https://github.com/MonoMod/MonoMod/actions/workflows/ci.yml/badge.svg)](https://github.com/MonoMod/MonoMod/actions/workflows/ci.yml)
| GitHub: All | NuGet: Patcher | NuGet: Utils | NuGet: RuntimeDetour | NuGet: HookGen |
|--|--|--|--|--|
@@ -16,24 +16,7 @@ General purpose .NET assembly modding "basework", powered by [cecil](https://git
## Sections
- [Introduction](#introduction)
-- [Using MonoMod](#using-monomod)
-- [Using ModInterop (ext)](/README-ModInterop.md)
-- [Using RuntimeDetour & HookGen (ext)](/README-RuntimeDetour.md)
-- [FAQ](#faq)
-
-### Special thanks to my [patrons on Patreon](https://www.patreon.com/0x0ade):
-- [Chad Yates](https://twitter.com/ChadCYates)
-- [Sc2ad](https://github.com/sc2ad)
-- Raegous
-- Chaser6
-- [Harrison Clarke](https://twitter.com/hay_guise)
-- [KyleTheScientist](https://www.twitch.tv/kylethescientist)
-- [Renaud Bédard](https://twitter.com/renaudbedard)
-- [leo60228](https://leo60228.space)
-- [Rubydragon](https://www.twitch.tv/rubydrag0n)
-- Holly Magala
-- [Jimmy Londo (iamdadbod)](https://www.youtube.com/iamdadbod)
-- [Artus Elias Meyer-Toms](https://twitter.com/artuselias)
+- [Using RuntimeDetour](docs/RuntimeDetour/Usage.md)
----
@@ -45,7 +28,7 @@ Mods / mod loaders for the following games are already using it in one way or an
- Celeste: [Everest](https://everestapi.github.io/)
- Risk of Rain 2: [BepInExPack (BepInEx + MonoMod + R2API)](https://thunderstore.io/package/bbepis/BepInExPack/)
- Enter the Gungeon: [Mod the Gungeon](https://modthegungeon.github.io/)
-- Rain World: [RainDB via Partiality](http://www.raindb.net/)
+- Rain World: [RainDB via BepInEx](http://www.raindb.net/)
- Totally Accurate Battle Simulator: [TABS-Multiplayer](https://github.com/Ceiridge/TABS-Multiplayer)
- Salt and Sanctuary: [Salt.Modding](https://github.com/seanpr96/Salt.Modding)
- Nimbatus: [Nimbatus-Mods via Partiality](https://github.com/OmegaRogue/Nimbatus-Mods)
@@ -75,6 +58,16 @@ It consists of the following **modular components**:
---
-### Debugging mods that use MonoMod
-
-See [Debugging](docs/Debugging.md).
\ No newline at end of file
+### Special thanks to my [patrons on Patreon](https://www.patreon.com/0x0ade):
+- [Chad Yates](https://twitter.com/ChadCYates)
+- [Sc2ad](https://github.com/sc2ad)
+- Raegous
+- Chaser6
+- [Harrison Clarke](https://twitter.com/hay_guise)
+- [KyleTheScientist](https://www.twitch.tv/kylethescientist)
+- [Renaud Bédard](https://twitter.com/renaudbedard)
+- [leo60228](https://leo60228.space)
+- [Rubydragon](https://www.twitch.tv/rubydrag0n)
+- Holly Magala
+- [Jimmy Londo (iamdadbod)](https://www.youtube.com/iamdadbod)
+- [Artus Elias Meyer-Toms](https://twitter.com/artuselias)
diff --git a/azure-pipelines-coretest.yml b/azure-pipelines-coretest.yml
deleted file mode 100644
index 18434389..00000000
--- a/azure-pipelines-coretest.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-parameters:
-- name: targetFramework
- type: string
- default: ''
-- name: buildConfiguration
- type: string
- default: 'Release'
-- name: arch
- type: string
- default: ''
-- name: condition
- type: string
- default: true
-- name: usePgo
- type: string
- default: false
-- name: pgoTimeout
- type: string
- default: 5
-
-steps:
-- ${{ if eq(length('${{parameters.arch}}'), 0) }}:
- - task: CmdLine@2
- condition: and(always(), ${{parameters.condition}})
- displayName: 'Test: core: ${{parameters.targetFramework}}'
- inputs:
- script: |
- dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}.trx"
- - task: PublishTestResults@2
- condition: and(always(), ${{parameters.condition}})
- displayName: 'Test: Publish: core: ${{parameters.targetFramework}}'
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}.trx'
- testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}}'
-
-- ${{ if ne(length('${{parameters.arch}}'), 0) }}:
- - task: CmdLine@2
- condition: and(always(), ${{parameters.condition}})
- displayName: 'Test: core: ${{parameters.targetFramework}}-${{parameters.arch}}'
- inputs:
- script: |
- dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} --arch '${{parameters.arch}}' -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.trx"
- - task: PublishTestResults@2
- condition: and(always(), ${{parameters.condition}})
- displayName: 'Test: Publish: core: ${{parameters.targetFramework}}-${{parameters.arch}}'
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.trx'
- testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}}-${{parameters.arch}}'
-
-- ${{ if and(eq('${{parameters.usePgo}}', 'true'), eq(length('${{parameters.arch}}'), 0)) }}:
- - task: CmdLine@2
- condition: and(always(), ${{parameters.condition}})
- timeoutInMinutes: ${{parameters.pgoTimeout}}
- displayName: 'Test: core: ${{parameters.targetFramework}} PGO'
- env:
- DOTNET_ReadyToRun: 0
- DOTNET_TC_QuickJitForLoops: 1
- DOTNET_TieredPGO: 1
- inputs:
- script: |
- dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}.trx"
- - task: PublishTestResults@2
- condition: and(always(), ${{parameters.condition}})
- displayName: 'Test: Publish: core: ${{parameters.targetFramework}}'
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}.pgo.trx'
- testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}} PGO'
-
-- ${{ if and(eq('${{parameters.usePgo}}', 'true'), ne(length('${{parameters.arch}}'), 0)) }}:
- - task: CmdLine@2
- condition: and(always(), ${{parameters.condition}})
- timeoutInMinutes: ${{parameters.pgoTimeout}}
- displayName: 'Test: core: ${{parameters.targetFramework}}-${{parameters.arch}} PGO'
- env:
- DOTNET_ReadyToRun: 0
- DOTNET_TC_QuickJitForLoops: 1
- DOTNET_TieredPGO: 1
- inputs:
- script: |
- dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} --arch '${{parameters.arch}}' -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.trx"
- - task: PublishTestResults@2
- condition: and(always(), ${{parameters.condition}})
- displayName: 'Test: Publish: core: ${{parameters.targetFramework}}-${{parameters.arch}}'
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.pgo.trx'
- testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}}-${{parameters.arch}} PGO'
\ No newline at end of file
diff --git a/azure-pipelines-docfx.ps1 b/azure-pipelines-docfx.ps1
deleted file mode 100644
index ca10e6af..00000000
--- a/azure-pipelines-docfx.ps1
+++ /dev/null
@@ -1,31 +0,0 @@
-$GitHubBotName=$args[0]
-$GitHubBotEmail=$args[1]
-$GitHubBotToken=$args[2]
-
-# PowerShell and git don't work well together.
-$ErrorActionPreference="Continue"
-
-Write-Output "Setting up config"
-$DocFXRepo="https://$GitHubBotToken@github.com/MonoMod/MonoMod.github.io.git"
-git config --global user.name $GitHubBotName 2>&1 | ForEach-Object { "$_" }
-git config --global user.email $GitHubBotEmail 2>&1 | ForEach-Object { "$_" }
-
-Write-Output "Setting up file hierarchy"
-git clone --recursive --branch docfx $DocFXRepo docfx 2>&1 | ForEach-Object { "$_" }
-Set-Location docfx
-git clone --recursive --branch master $DocFXRepo _site 2>&1 | ForEach-Object { "$_" }
-
-Write-Output "Installing DocFX"
-choco install docfx -y
-
-Write-Output "Running docfx build"
-docfx metadata
-docfx build
-
-Write-Output "Pushing updated _site to master branch on GitHub"
-Set-Location _site
-git add . 2>&1 | ForEach-Object { "$_" }
-git commit --allow-empty -m "Rebuild (automatic commit via Azure Pipelines)" 2>&1 | ForEach-Object { "$_" }
-git push 2>&1 | ForEach-Object { "$_" }
-
-Write-Output "Done"
diff --git a/azure-pipelines-postbuild.yml b/azure-pipelines-postbuild.yml
deleted file mode 100644
index 9b1d49ae..00000000
--- a/azure-pipelines-postbuild.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-parameters:
- targetFramework: ''
- buildConfiguration: 'Release'
-
-steps:
-- task: CopyFiles@2
- displayName: 'Artifacts: Pack: ${{parameters.targetFramework}}'
- continueOnError: true
- inputs:
- sourceFolder: '$(Agent.BuildDirectory)'
- # the leading **/ shouldn't be necessary, but it seems to be
- contents: |
- **/artifacts/bin/**/${{parameters.buildConfiguration}}/${{parameters.targetFramework}}/**/*
- !**/MonoMod.FrameworkTests/**/*
- !**/Monomod.Backports.Filter/**/*
- !**/Monomod.ILHelpers.Patcher/**/*
- !**/MonoMod.UnitTest/**/*
- !**/xunit.*
- !**/testhost*
- !**/System.*
- !**/Microsoft.*
- targetFolder: '$(Build.ArtifactStagingDirectory)/${{parameters.targetFramework}}'
- cleanTargetFolder: true
- overWrite: true
- flattenFolders: true
-- task: PublishBuildArtifacts@1
- displayName: 'Artifacts: Publish: ${{parameters.targetFramework}}'
- continueOnError: true
- inputs:
- pathtoPublish: '$(Build.ArtifactStagingDirectory)/${{parameters.targetFramework}}'
- artifactName: '$(artifactPrefix)${{parameters.targetFramework}}$(artifactSuffix)'
- publishLocation: 'Container'
diff --git a/azure-pipelines-prepushcommon.ps1 b/azure-pipelines-prepushcommon.ps1
deleted file mode 100644
index a97dc263..00000000
--- a/azure-pipelines-prepushcommon.ps1
+++ /dev/null
@@ -1,10 +0,0 @@
-# MonoMod.Common doesn't exist.
-#if ($(git rev-parse "@:MonoMod.Common") -eq $(git rev-parse "@~:MonoMod.Common")) {
-# Write-Output "MonoMod.Common wasn't changed."
-# for ($i = 0; $i -lt $args.Length; $i++) {
-# Write-Output "Deleting $($args[$i])"
-# Remove-Item -Path "$($args[$i])"
-# }
-#} else {
-# Write-Output "MonoMod.Common was changed."
-#}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index 8e2176a4..00000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,302 +0,0 @@
-strategy:
- matrix:
- windows:
- jobArchName: 'Windows'
- imageName: 'windows-latest'
- artifactPrefix: ''
- artifactSuffix: ''
- testers: ';fx;core2.1;core3;core3.1;core5;core6;core7;core8;'
- testx86: 'true'
- generateDocFX: 'false'
- pushNuGet: 'true'
- linux:
- jobArchName: 'Linux'
- imageName: 'ubuntu-latest'
- artifactPrefix: '~dbg.linux.'
- artifactSuffix: ''
- testers: ';mono;core2.1;core3;core3.1;core5;core6;core7;core8;'
- testx86: 'false'
- generateDocFX: 'false'
- pushNuGet: 'false'
- macos:
- jobArchName: 'macOS'
- imageName: 'macOS-latest'
- artifactPrefix: '~dbg.macos.'
- artifactSuffix: ''
- testers: ';mono;core2.1;core3;core3.1;core5;core6;core7;core8;'
- testx86: 'false'
- generateDocFX: 'false'
- pushNuGet: 'false'
- maxParallel: 4
-
-variables:
- solution: '**/*.sln'
- buildConfiguration: 'Release'
- buildPlatform: 'Any CPU'
- xunitVer: '2.4.2'
- xunit: '{0}{1}/.nuget/packages/xunit.runner.console/$(xunitVer)/tools/{2}/xunit.console.{3} artifacts/bin/MonoMod.UnitTest/Release/{6}/MonoMod.UnitTest.dll -xml testresults.{4}.{6}.xml {5}'
-
-name: '$(Date:y.M.d).$(Rev:r)'
-
-pool:
- vmImage: '$(imageName)'
-
-steps:
-- checkout: self
- clean: false
- fetchDepth: 2
- lfs: false
- submodules: recursive
-
-# Run pre-build steps.
-- task: UseDotNet@2
- displayName: 'Install .NET Core SDK 2.1.x'
- inputs:
- packageType: sdk
- version: '2.1.x'
-- task: UseDotNet@2
- displayName: 'Install .NET Core SDK 3.0.x'
- inputs:
- packageType: sdk
- version: '3.0.x'
-- task: UseDotNet@2
- displayName: 'Install .NET Core SDK 3.1.x'
- inputs:
- packageType: sdk
- version: '3.1.x'
-- task: UseDotNet@2
- condition: contains(variables.testers, ';core5;')
- displayName: 'Install .NET SDK 5.0.x'
- inputs:
- packageType: sdk
- version: '5.0.x'
-- task: UseDotNet@2
- condition: contains(variables.testers, ';core6;')
- displayName: 'Install .NET SDK 6.0.x'
- inputs:
- packageType: sdk
- version: '6.0.x'
-- task: UseDotNet@2
- condition: contains(variables.testers, ';core7;')
- displayName: 'Install .NET SDK 7.0.x'
- inputs:
- packageType: sdk
- version: '7.0.x'
-- task: UseDotNet@2
- condition: contains(variables.testers, ';core8;')
- displayName: 'Install .NET SDK 8.0.x'
- inputs:
- packageType: sdk
- version: '8.0.x'
- includePreviewVersions: true
-- task: NuGetToolInstaller@1
- displayName: 'Update NuGet'
- inputs:
- checkLatest: true
-- task: DotNetCoreCLI@2
- displayName: 'dotnet: Restore'
- inputs:
- command: 'restore'
- arguments: '-p:XunitVersion="$(xunitVer)"'
-
-# Build using core. Mono's msbuild started to cause too many issues.
-- task: DotNetCoreCLI@2
- displayName: 'dotnet: Build'
- inputs:
- command: 'build'
- arguments: '-c $(buildConfiguration) -p:XunitVersion="$(xunitVer)" -p:VersionSuffix="daily.$(Build.BuildNumber)" -p:ContinuousIntegrationBuild=true -tl:off'
-
-# Create and "publish" main artifacts.
-- template: 'azure-pipelines-postbuild.yml'
- parameters:
- targetFramework: 'net35'
-- template: 'azure-pipelines-postbuild.yml'
- parameters:
- targetFramework: 'net452'
-- template: 'azure-pipelines-postbuild.yml'
- parameters:
- targetFramework: 'netstandard2.0'
-- template: 'azure-pipelines-postbuild.yml'
- parameters:
- targetFramework: 'net5.0'
-- template: 'azure-pipelines-postbuild.yml'
- parameters:
- targetFramework: 'net6.0'
-- template: 'azure-pipelines-postbuild.yml'
- parameters:
- targetFramework: 'net7.0'
-# - template: 'azure-pipelines-postbuild.yml'
-# parameters:
-# targetFramework: 'net8.0'
-
-# Create and "publish" nupkg artifacts.
-- task: DotNetCoreCLI@2
- displayName: 'Artifacts: Pack: nupkgs'
- continueOnError: true
- inputs:
- #command: 'pack'
- command: 'custom' # if you use command: pack, then you *have* to set packagesToPack, despite not providing it doing the right thing
- custom: 'pack'
- arguments: '-c $(buildConfiguration) -p:XunitVersion="$(xunitVer)" -p:VersionSuffix="daily.$(Build.BuildNumber)" -p:ContinuousIntegrationBuild=true'
- nobuild: true
- configuration: '$(buildConfiguration)'
- versioningScheme: 'byBuildNumber'
- #packagesToPack: 'MonoMod@(|.Common|.Utils|.RuntimeDetour|.RuntimeDetour.HookGen)/*.csproj'
- packDirectory: '$(Build.ArtifactStagingDirectory)/nupkgs'
-- task: PublishBuildArtifacts@1
- displayName: 'Artifacts: Publish: nupkgs'
- continueOnError: true
- inputs:
- pathtoPublish: 'artifacts/packages/$(buildConfiguration)'
- artifactName: '$(artifactPrefix)nupkgs$(artifactSuffix)'
- publishLocation: 'Container'
-- task: NuGetCommand@2
- condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true'))
- displayName: 'Artifacts: Push: nupkgs'
- inputs:
- command: push
- packagesToPush: 'artifacts/packages/$(buildConfiguration)/*.nupkg'
- publishVstsFeed: '572c97eb-dbaa-4a55-90e5-1d05431535bd/72ad568d-c548-4599-8b0a-9ea52b45bbbd'
-#- task: PowerShell@2
-# condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true'))
-# displayName: 'Artifacts: Prepare: MonoMod.Common to nuget.org'
-# continueOnError: true
-# inputs:
-# filePath: 'azure-pipelines-prepushcommon.ps1'
-# arguments: '$(Build.ArtifactStagingDirectory)/nupkgs/MonoMod.Common.*.nupkg'
-#- task: NuGetCommand@2
-# condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true'))
-# displayName: 'Artifacts: Push: MonoMod.Common to nuget.org'
-# inputs:
-# command: 'push'
-# packagesToPush: '$(Build.ArtifactStagingDirectory)/nupkgs/MonoMod.Common.*.nupkg'
-# nuGetFeedType: 'external'
-# publishFeedCredentials: 'NuGet MonoMod'
-
-# Run tests.
-# I've tried to simplify this mess. It's impossible. --ade
-- task: CmdLine@2
- condition: and(always(), contains(variables.testers, ';fx;'))
- displayName: 'Test: fx: net452'
- inputs:
- script: ${{format(variables.xunit, '', '%userprofile%', 'net452', 'exe', 'fx', '', 'net46')}}
-- task: PublishTestResults@2
- condition: and(always(), contains(variables.testers, ';fx;'))
- displayName: 'Test: Publish: fx: net46'
- inputs:
- testResultsFormat: 'xUnit'
- testResultsFiles: '**/testresults.fx.net46.xml'
- testRunTitle: 'Tests @ $(jobArchName) fx net46'
-
-- task: CmdLine@2
- condition: and(always(), contains(variables.testers, ';mono;'))
- displayName: 'Test: mono: net46'
- inputs:
- script: ${{format(variables.xunit, 'mono --debug ', '~', 'net452', 'exe', 'mono', '', 'net46')}}
-- task: PublishTestResults@2
- condition: and(always(), contains(variables.testers, ';mono;'))
- displayName: 'Test: Publish: mono: net46'
- inputs:
- testResultsFormat: 'xUnit'
- testResultsFiles: '**/testresults.mono.net46.xml'
- testRunTitle: 'Tests @ $(jobArchName) mono net46'
-
-- task: CmdLine@2
- condition: and(always(), contains(variables.testers, ';monoslow;'))
- displayName: 'Test: monoslow: net46'
- inputs:
- script: ${{format(variables.xunit, 'mono --debug ', '~', 'net452', 'exe', 'monoslow', '-parallel none -appdomains denied -verbose', 'net46')}}
-- task: PublishTestResults@2
- condition: and(always(), contains(variables.testers, ';monoslow;'))
- displayName: 'Test: Publish: monoslow: net46'
- inputs:
- testResultsFormat: 'xUnit'
- testResultsFiles: '**/testresults.monoslow.net46.xml'
- testRunTitle: 'Tests @ $(jobArchName) monoslow net46'
-
-# All of the .NET Core tests run via azure-pipelines-coretest.yml, and have an optional 'arch' parameter to allow specific arch selection
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core2.1;'))
- targetFramework: 'netcoreapp2.1'
-
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core3;'))
- targetFramework: 'netcoreapp3.0'
- arch: 'x64'
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(contains(variables.testers, ';core3;'), eq(variables.testx86, 'true'))
- targetFramework: 'netcoreapp3.0'
- arch: 'x86'
-
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core3.1;'))
- targetFramework: 'netcoreapp3.1'
- arch: 'x64'
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(contains(variables.testers, ';core3.1;'), eq(variables.testx86, 'true'))
- targetFramework: 'netcoreapp3.1'
- arch: 'x86'
-
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core5;'))
- targetFramework: 'net5.0'
- arch: 'x64'
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(contains(variables.testers, ';core5;'), eq(variables.testx86, 'true'))
- targetFramework: 'net5.0'
- arch: 'x86'
-
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core6;'))
- targetFramework: 'net6.0'
- arch: 'x64'
- usePgo: 'true'
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(contains(variables.testers, ';core6;'), eq(variables.testx86, 'true'))
- targetFramework: 'net6.0'
- arch: 'x86'
- usePgo: 'true'
-
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core7;'))
- targetFramework: 'net7.0'
- arch: 'x64'
- usePgo: 'true'
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(contains(variables.testers, ';core7;'), eq(variables.testx86, 'true'))
- targetFramework: 'net7.0'
- arch: 'x86'
- usePgo: 'true'
-
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(true, contains(variables.testers, ';core8;'))
- targetFramework: 'net8.0'
- arch: 'x64'
- usePgo: 'true'
-- template: 'azure-pipelines-coretest.yml'
- parameters:
- condition: and(contains(variables.testers, ';core8;'), eq(variables.testx86, 'true'))
- targetFramework: 'net8.0'
- arch: 'x86'
- usePgo: 'true'
-
-# Rebuild and publish the DocFX page.
-- task: PowerShell@2
- condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.generateDocFX, 'true'))
- displayName: 'DocFX'
- continueOnError: true
- inputs:
- filePath: 'azure-pipelines-docfx.ps1'
- arguments: '$(GitHubBotName) $(GitHubBotEmail) $(GitHubBotToken)'
diff --git a/docfx/.gitignore b/docfx/.gitignore
new file mode 100644
index 00000000..2781f6d5
--- /dev/null
+++ b/docfx/.gitignore
@@ -0,0 +1,9 @@
+###############
+# folder #
+###############
+/**/DROP/
+/**/TEMP/
+/**/packages/
+/**/bin/
+/**/obj/
+_site
diff --git a/docfx/CNAME b/docfx/CNAME
new file mode 100644
index 00000000..0fa7e435
--- /dev/null
+++ b/docfx/CNAME
@@ -0,0 +1 @@
+monomod.dev
diff --git a/docfx/api/.gitignore b/docfx/api/.gitignore
new file mode 100644
index 00000000..f798527e
--- /dev/null
+++ b/docfx/api/.gitignore
@@ -0,0 +1,5 @@
+###############
+# temp file #
+###############
+*.yml
+.manifest
diff --git a/docfx/api/index.md b/docfx/api/index.md
new file mode 100644
index 00000000..e69de29b
diff --git a/docfx/articles/intro.md b/docfx/articles/intro.md
new file mode 100644
index 00000000..901a16a4
--- /dev/null
+++ b/docfx/articles/intro.md
@@ -0,0 +1 @@
+# Add your introductions here!
diff --git a/docfx/articles/toc.yml b/docfx/articles/toc.yml
new file mode 100644
index 00000000..f64352ce
--- /dev/null
+++ b/docfx/articles/toc.yml
@@ -0,0 +1,2 @@
+- name: Introduction
+ href: intro.md
diff --git a/docfx/docfx.csproj b/docfx/docfx.csproj
new file mode 100644
index 00000000..e41366b7
--- /dev/null
+++ b/docfx/docfx.csproj
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docfx/docfx.json b/docfx/docfx.json
new file mode 100644
index 00000000..eb2cf141
--- /dev/null
+++ b/docfx/docfx.json
@@ -0,0 +1,91 @@
+{
+ "metadata": [
+ {
+ "src": [
+ {
+ "src": "..",
+ "files": [
+ "src/**/MonoMod*.csproj"
+ ],
+ "exclude": [
+ "**/MonoMod.FrameworkTests.*",
+ "**/MonoMod.SourceGen.*.*",
+ "**/MonoMod.UnitTest.*",
+ "**/MonoMod.Backports.Tasks.*",
+ "**/MonoMod.Patcher.*",
+ "**/MonoMod.ILHelpers.Patcher.*",
+ "**/MonoMod.DebugIL.*",
+ "**/MonoMod.RuntimeDetour.HookGen.*"
+ ]
+ }
+ ],
+ "dest": "api",
+ "disableGitFeatures": false,
+ "disableDefaultFilter": false,
+ "shouldSkipMarkup": true,
+ "outputFormat": "mref",
+ "filter": "filterConfig.yml",
+ "categoryLayout": "nested",
+ "namespaceLayout": "flattened",
+ "memberLayout": "separatePages",
+ "enumSortOrder": "declaringOrder"
+ }
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "api/index.md"
+ ]
+ },
+ {
+ "files": [
+ "articles/**.md",
+ "articles/**/toc.yml",
+ "toc.yml",
+ "*.md"
+ ]
+ },
+ {
+ "src": "../docs",
+ "dest": "docs",
+ "files": [
+ "**.md",
+ "**/toc.yml"
+ ]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "images/**",
+ "CNAME"
+ ]
+ }
+ ],
+ "overwrite": [
+ {
+ "files": [
+ "apidoc/**.md"
+ ],
+ "exclude": [
+ "obj/**",
+ "_site/**"
+ ]
+ }
+ ],
+ "dest": "_site",
+ "globalMetadataFiles": ["globalmeta.json"],
+ "fileMetadataFiles": [],
+ "template": [
+ "default", "modern"
+ ],
+ "postProcessors": ["ExtractSearchIndex"],
+ "markdownEngineName": "markdig",
+ "noLangKeyword": false,
+ "keepFileLink": false,
+ "cleanupCacheHistory": false,
+ "disableGitFeatures": false
+ }
+}
\ No newline at end of file
diff --git a/docfx/filterConfig.yml b/docfx/filterConfig.yml
new file mode 100644
index 00000000..cad31780
--- /dev/null
+++ b/docfx/filterConfig.yml
@@ -0,0 +1,7 @@
+apiRules:
+- exclude:
+ uidRegex: ^System.*
+ type: Namespace
+- exclude:
+ uidRegex: ^Iced.*
+ type: Namespace
\ No newline at end of file
diff --git a/docfx/globalmeta.json b/docfx/globalmeta.json
new file mode 100644
index 00000000..928b553e
--- /dev/null
+++ b/docfx/globalmeta.json
@@ -0,0 +1,13 @@
+{
+ "_appTitle": "MonoMod",
+ "_appName": "MonoMod",
+ "_appFooter": "Yet another C# modding swiss army knife.",
+ "_description": "A C# modding swiss army knife, powered by cecil.",
+ "author": "0x0ade and contributors",
+ "url": "https://monomod.github.io/",
+ "image": "https://monomod.github.io/favicon.png",
+ "_appLogoPath": "images/logo-small.png",
+ "_appFaviconPath": "images/favicon.png",
+ "_enableSearch": true,
+ "_enableNewTab": true
+}
\ No newline at end of file
diff --git a/docfx/images/favicon.png b/docfx/images/favicon.png
new file mode 100644
index 00000000..5aadb2ad
Binary files /dev/null and b/docfx/images/favicon.png differ
diff --git a/docfx/images/logo-small.png b/docfx/images/logo-small.png
new file mode 100644
index 00000000..fedf971d
Binary files /dev/null and b/docfx/images/logo-small.png differ
diff --git a/docfx/index.md b/docfx/index.md
new file mode 100644
index 00000000..53c77a07
--- /dev/null
+++ b/docfx/index.md
@@ -0,0 +1 @@
+[!INCLUDE [readme](../README.md)]
\ No newline at end of file
diff --git a/docfx/toc.yml b/docfx/toc.yml
new file mode 100644
index 00000000..3bcca430
--- /dev/null
+++ b/docfx/toc.yml
@@ -0,0 +1,10 @@
+- name: Home
+ href: index.md
+- name: API
+ href: api/
+- name: DevDocs
+ href: docs/
+- name: GitHub
+ href: https://github.com/MonoMod
+- name: Discord
+ href: https://discord.gg/jm7GCZB
\ No newline at end of file
diff --git a/docs/Core/index.md b/docs/Core/index.md
new file mode 100644
index 00000000..3b67f455
--- /dev/null
+++ b/docs/Core/index.md
@@ -0,0 +1,5 @@
+# MonoMod.Core
+
+- [Discussion about the CoreCLR JIT hook design](CoreCLRJitHooks.md)
+- [`INativeExceptionHelper`](NativeExceptionHandler.md)
+- [MonoMod.Core's Architecture](Architecture.md)
diff --git a/docs/Core/toc.yml b/docs/Core/toc.yml
new file mode 100644
index 00000000..543d8301
--- /dev/null
+++ b/docs/Core/toc.yml
@@ -0,0 +1,8 @@
+href: index.md
+items:
+- name: Discussion about the CoreCLR JIT hook design
+ href: CoreCLRJitHooks.md
+- name: INativeExceptionHelper
+ href: NativeExceptionHandler.md
+- name: MonoMod.Core's Architecture
+ href: Architecture.md
diff --git a/docs/README.ILHelpers.md b/docs/README.ILHelpers.md
index b2033350..cf52b15a 100644
--- a/docs/README.ILHelpers.md
+++ b/docs/README.ILHelpers.md
@@ -1,13 +1 @@
-# `MonoMod.ILHelpers`
-
-`MonoMod.ILHelpers` is a collection of helpers manually implemented in IL.
-
-Notably, this contains a backport of `System.Runtime.CompilerServices.Unsafe`, as it exists in .NET 6, to all older
-runtimes. This means that any environment which *also* provides that class which is older than .NET 6 will require
-an `extern alias` to be able to use properly.
-
-## Notable APIs
-
-- `System.Runtime.CompilerServices.Unsafe`
-- `MonoMod.ILHelpers`
--
\ No newline at end of file
+# DO NOT REFERENCE THIS PACKAGE DIRECTLY! Reference MonoMod.Backports instead.
\ No newline at end of file
diff --git a/docs/RuntimeDetour.HookGen/index.md b/docs/RuntimeDetour.HookGen/index.md
new file mode 100644
index 00000000..1a64eb10
--- /dev/null
+++ b/docs/RuntimeDetour.HookGen/index.md
@@ -0,0 +1,3 @@
+# MonoMod.RuntimeDetour.HookGen
+
+* [Using HookGen](Usage.md)
diff --git a/docs/RuntimeDetour.HookGen/toc.yml b/docs/RuntimeDetour.HookGen/toc.yml
new file mode 100644
index 00000000..f97f6084
--- /dev/null
+++ b/docs/RuntimeDetour.HookGen/toc.yml
@@ -0,0 +1,4 @@
+href: index.md
+items:
+- name: Using HookGen
+ href: Usage.md
diff --git a/docs/RuntimeDetour/implementation/index.md b/docs/RuntimeDetour/implementation/index.md
new file mode 100644
index 00000000..14c856b7
--- /dev/null
+++ b/docs/RuntimeDetour/implementation/index.md
@@ -0,0 +1,3 @@
+# Implementation
+
+* [ChainHotPatching](ChainHotPatching.md)
diff --git a/docs/RuntimeDetour/implementation/toc.yml b/docs/RuntimeDetour/implementation/toc.yml
new file mode 100644
index 00000000..4fb1fe92
--- /dev/null
+++ b/docs/RuntimeDetour/implementation/toc.yml
@@ -0,0 +1,4 @@
+href: index.md
+items:
+- name: ChainHotPatching
+ href: ChainHotPatching.md
diff --git a/docs/RuntimeDetour/index.md b/docs/RuntimeDetour/index.md
new file mode 100644
index 00000000..d21d5a3f
--- /dev/null
+++ b/docs/RuntimeDetour/index.md
@@ -0,0 +1,4 @@
+# MonoMod.RuntimeDetour
+
+- [Implementation](implementation/index.md)
+- [Using RuntimeDetour](Usage.md)
diff --git a/docs/RuntimeDetour/toc.yml b/docs/RuntimeDetour/toc.yml
new file mode 100644
index 00000000..e75c196c
--- /dev/null
+++ b/docs/RuntimeDetour/toc.yml
@@ -0,0 +1,7 @@
+# This is an automatically generated file
+href: index.md
+items:
+- name: Implementation
+ href: implementation/toc.yml
+- name: Using RuntimeDetour
+ href: Usage.md
diff --git a/docs/RuntimeIssueNotes.md b/docs/RuntimeIssueNotes.md
new file mode 100644
index 00000000..0bdbb3f2
--- /dev/null
+++ b/docs/RuntimeIssueNotes.md
@@ -0,0 +1,128 @@
+# Notes on issues in various runtime versions
+
+Martin, this is wrong.
+
+## `sizeof` IL opcode does not work with generic parameters on old Mono
+
+The title says it all. `sizeof` works fine with all other type-specs, but with generic parameters specifically,
+it always returns the system pointer size.
+
+The relevant code is in `metadata/metadata.c`, in `mono_type_size` (which `sizeof` correctly embeds as a constant):
+
+```c
+int
+mono_type_size (MonoType *t, int *align)
+{
+ // ...
+
+ switch (t->type){
+ // ...
+ case MONO_TYPE_VAR:
+ case MONO_TYPE_MVAR:
+ /* FIXME: Martin, this is wrong. */
+ *align = __alignof__(gpointer);
+ return sizeof (gpointer);
+ // ...
+ }
+
+ // ...
+}
+```
+
+## `fixed` on strings in old Mono
+
+Some old versions of Mono have broken `conv.u` instruction handling.
+
+The following code will crash those old versions with an assert in the JIT's local propagation routine:
+
+```csharp
+fixed (char* pStr = "some string")
+{
+ // ...
+}
+```
+
+This is because the sequence that Roslyn emits for `fixed` over a string is this:
+
+```il
+ .locals (
+ string pinned stringLocalMarkedPinned,
+ char* ptrLocal
+ )
+
+ // ...
+
+ // load string object onto the stack...
+ stloc stringLocalMarkedPinned
+ ldloc stringLocalMarkedPinned
+ conv.u
+ stloc ptrLocal
+ ldloc ptrLocal
+ brfalse.s PTR_NULL
+
+ ldloc ptrLocal
+ call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData()
+ add
+ stloc ptrLocal
+
+PTR_NULL:
+ // code using the pointer in ptrLocal
+```
+
+Importantly, this sequence uses `conv.u` on a string object to convert a value of type `O` to a value of type `U`
+(a.k.a. `nuint` or `UIntPtr`), giving the address of the object, then uses `RuntimeHelpers.OffsetToStringData` to
+offset that to the start of the string data.
+
+New runtimes expose `GetPinnableReference()` on `string`, and if it's present, Roslyn will use that instead. However,
+the versions of Mono that this is a problem with (such as the version Unity 5.x uses) expose the .NET 3.5 API surface
+which does *not* include `GetPinnableReference()`.
+
+This fails because the JIT's importer has an incomplete implementation of `conv.u`.
+
+The relevant code is in `mini/method-to-ir.c`, in the `type_from_op` function:
+
+```c
+// ...
+switch (ins->opcode) {
+// ...
+case CEE_CONV_U:
+ ins->type = STACK_PTR;
+ switch (src1->type) {
+ case STACK_I4:
+ ins->opcode = OP_ICONV_TO_U;
+ break;
+ case STACK_PTR:
+ case STACK_MP:
+#if SIZEOF_REGISTER == 8
+ ins->opcode = OP_LCONV_TO_U;
+#else
+ ins->opcode = OP_MOVE;
+#endif
+ break;
+ case STACK_I8:
+ ins->opcode = OP_LCONV_TO_U;
+ break;
+ case STACK_R8:
+ ins->opcode = OP_FCONV_TO_U;
+ break;
+ }
+ break;
+// ...
+}
+// ...
+```
+
+In the problematic case, `src1->type` is `STACK_OBJ`, which is not handled, and so `ins->opcode` remains the IL
+opcode for `conv.u`, as opposed to a Mono IR opcode. This then causes an assertion failure in `mini/local-propagation.c`
+in `mono_local_cprop`:
+
+```c
+g_assert (ins->opcode > MONO_CEE_LAST);
+```
+
+Which, of course, fails, because `ins->opcode` is still `CEE_CONV_U`, which is less than `MONO_CEE_LAST`.
+
+### Workarounds
+
+1. Use an array, like `new char[] { /* ... */ }`.
+2. Convert the string to a `ReadOnlySpan` first, like `str.AsSpan()`.
diff --git a/docs/Utils/index.md b/docs/Utils/index.md
new file mode 100644
index 00000000..4aaea02d
--- /dev/null
+++ b/docs/Utils/index.md
@@ -0,0 +1,3 @@
+# MonoMod.Utils
+
+* [Using ModInterop](ModInterop.md)
diff --git a/docs/Utils/toc.yml b/docs/Utils/toc.yml
new file mode 100644
index 00000000..d299175f
--- /dev/null
+++ b/docs/Utils/toc.yml
@@ -0,0 +1,4 @@
+href: index.md
+items:
+- name: Using ModInterop
+ href: ModInterop.md
diff --git a/docs/docs.csproj b/docs/docs.csproj
new file mode 100644
index 00000000..4d7b3159
--- /dev/null
+++ b/docs/docs.csproj
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..cd2614cf
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,20 @@
+# Conceptual Documentation Index
+
+- [Debugging With MonoMod](Debugging.md)
+- [MonoMod Configuration Switches](Switches.md)
+
+## Per-package
+
+- [MonoMod.Core](Core/index.md)
+- [MonoMod.RuntimeDetour](RuntimeDetour/index.md)
+- [MonoMod.RuntimeDetour.HookGen](RuntimeDetour.HookGen/index.md)
+- [MonoMod.Utils](Utils/index.md)
+
+## Package READMEs
+
+- [MonoMod.Backports](README.Backports.md)
+- [MonoMod.ILHelpers](README.ILHelpers.md)
+- [MonoMod.Core](README.Core.md)
+- [MonoMod.Patcher](README.Patcher.md)
+- [MonoMod.RuntimeDetour](README.RuntimeDetour.md)
+- [MonoMod.Utils](README.Utils.md)
diff --git a/docs/toc.yml b/docs/toc.yml
new file mode 100644
index 00000000..671fd9d6
--- /dev/null
+++ b/docs/toc.yml
@@ -0,0 +1,33 @@
+name: Conceptual Documentation
+href: index.md
+items:
+- name: Debugging With MonoMod
+ href: Debugging.md
+ items:
+ - name: Configuration Switches
+ href: Switches.md
+- name: Per-package
+ items:
+ - name: MonoMod.Core
+ href: Core/toc.yml
+ - name: MonoMod.RuntimeDetour
+ href: RuntimeDetour/toc.yml
+ - name: MonoMod.RuntimeDetour.HookGen
+ href: RuntimeDetour.HookGen/toc.yml
+ - name: MonoMod.Utils
+ href: Utils/toc.yml
+- name: Package READMEs
+ items:
+ - name: MonoMod.Backports
+ href: README.Backports.md
+ - name: MonoMod.ILHelpers
+ href: README.ILHelpers.md
+ - name: MonoMod.Core
+ href: README.Core.md
+ - name: MonoMod.Patcher
+ href: README.Patcher.md
+ - name: MonoMod.RuntimeDetour
+ href: README.RuntimeDetour.md
+ - name: MonoMod.Utils
+ href: README.Utils.md
+- href: RuntimeIssueNotes.md
\ No newline at end of file
diff --git a/external/iced.props b/external/iced.props
index 73226c4f..b6f7645d 100644
--- a/external/iced.props
+++ b/external/iced.props
@@ -10,7 +10,7 @@
true
false
true
- false
+
<_MMTargetFrameworks>$(TargetFrameworks)
<_MMDefineConsts>$(DefineConstants)
diff --git a/global.json b/global.json
index 7ddc0f3f..980041e3 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,11 @@
{
"sdk": {
"allowPrerelease": true,
- "rollForward": "latestMinor",
- "version": "8.0.100"
+ "rollForward": "latestPatch",
+ "version": "8.0.301"
+ },
+ "msbuild-sdks": {
+ "Microsoft.Build.NoTargets": "3.7.56",
+ "Microsoft.NET.Sdk.IL": "8.0.0"
}
}
\ No newline at end of file
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 00000000..f40e5a2c
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Common/Common.csproj b/src/Common/Common.csproj
index d58e8520..d6af450e 100644
--- a/src/Common/Common.csproj
+++ b/src/Common/Common.csproj
@@ -1,2 +1,2 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/Common/UnsafeAlias.cs b/src/Common/UnsafeAlias.cs
index 7d283c3c..29216bec 100644
--- a/src/Common/UnsafeAlias.cs
+++ b/src/Common/UnsafeAlias.cs
@@ -4,8 +4,8 @@
global using ilhelpers::MonoMod;
-#if !NET6_0_OR_GREATER
-// Any time we want to use Unsafe, we want ours, not the BCL's
+#if !NET6_0_OR_GREATER && (NETSTANDARD2_1_OR_GREATER || NETCOREAPP || NET)
+// Any time we want to use Unsafe, we want ours, not the BCL's. Note that we need these funky defs because the location of Unsafe moves between versions.
// I would actually rather move the BCL assembly defining it into an alias, but that doesn't seem to be particularly viable
global using Unsafe = ilhelpers::System.Runtime.CompilerServices.Unsafe;
#else
diff --git a/src/MonoMod.Backports/CompatibilitySuppressions.xml b/src/MonoMod.Backports/CompatibilitySuppressions.xml
index 24315c06..8674ead1 100644
--- a/src/MonoMod.Backports/CompatibilitySuppressions.xml
+++ b/src/MonoMod.Backports/CompatibilitySuppressions.xml
@@ -4,219 +4,87 @@
CP0002
M:System.ValueTuple.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`1.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`2.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`3.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`4.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`5.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`6.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`7.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
CP0002
M:System.ValueTuple`8.#ctor
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`1.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`2.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`3.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`4.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`5.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`6.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`7.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple`8.#ctor
- lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
- true
-
-
- CP0002
- M:System.ValueTuple.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`1.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`2.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`3.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`4.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`5.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`6.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`7.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
-
- CP0002
- M:System.ValueTuple`8.#ctor
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
CP0002
M:System.HashCode.AddBytes(System.ReadOnlySpan{System.Byte})
- ref/netstandard2.0/MonoMod.Backports.dll
- ref/netcoreapp2.1/MonoMod.Backports.dll
+ lib/netstandard2.0/MonoMod.Backports.dll
+ lib/netcoreapp2.1/MonoMod.Backports.dll
CP0002
M:System.HashCode.AddBytes(System.ReadOnlySpan{System.Byte})
- ref/netstandard2.0/MonoMod.Backports.dll
- ref/netstandard2.1/MonoMod.Backports.dll
+ lib/netstandard2.0/MonoMod.Backports.dll
+ lib/netstandard2.1/MonoMod.Backports.dll
CP0008
T:System.Runtime.CompilerServices.TupleElementNamesAttribute
- lib/net452/MonoMod.Backports.dll
+ lib/net35/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
- true
- CP0008
- T:System.Runtime.CompilerServices.TupleElementNamesAttribute
+ CP1002
+ mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes
lib/net452/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
+ lib/net452/MonoMod.Backports.dll
true
-
- CP0008
- T:System.Runtime.CompilerServices.TupleElementNamesAttribute
- ref/net35/MonoMod.Backports.dll
- ref/net452/MonoMod.Backports.dll
-
CP1002
mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes
ref/net452/MonoMod.Backports.dll
lib/net452/MonoMod.Backports.dll
+ true
\ No newline at end of file
diff --git a/src/MonoMod.Backports/Directory.Build.targets b/src/MonoMod.Backports/Directory.Build.targets
index 34ae00e8..c9b9a8cf 100644
--- a/src/MonoMod.Backports/Directory.Build.targets
+++ b/src/MonoMod.Backports/Directory.Build.targets
@@ -2,22 +2,20 @@
-
+
true
-
- all;buildTransitive
- all;buildTransitive
- none
- false
- true
- false
-
+
+
+
+ $([MSBuild]::NormalizePath('$(NuGetPackageRoot)', 'system.collections.immutable', '6.0.0'))
+
+
diff --git a/src/MonoMod.Backports/MonoMod.Backports.csproj b/src/MonoMod.Backports/MonoMod.Backports.csproj
index 47a43881..2de1a934 100644
--- a/src/MonoMod.Backports/MonoMod.Backports.csproj
+++ b/src/MonoMod.Backports/MonoMod.Backports.csproj
@@ -1,17 +1,19 @@
- $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1
+
+ $(BackportsTargetFrameworks)
false
- true
+ false
+ false
false
false
- 1.1.0
- 1.0.0
+ 1.1.2
+ 1.1.0
@@ -27,6 +29,13 @@
or ('$(MMTFKind)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals('$(MMTFVersion)','2.1')))">true
+
+
+
+ ilhelpers
+
+
+
diff --git a/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs b/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs
new file mode 100644
index 00000000..44cb4915
--- /dev/null
+++ b/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs
@@ -0,0 +1,192 @@
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP || NET
+#define UNSAFE_IN_ILHELPERS
+#endif
+
+extern alias ilhelpers;
+
+// Sometimes these global usings are unused. That's fine.
+#pragma warning disable IDE0005
+
+// Global usings
+global using ilhelpers::MonoMod;
+
+#if UNSAFE_IN_ILHELPERS && !NET6_0_OR_GREATER
+global using Unsafe = ilhelpers::System.Runtime.CompilerServices.Unsafe;
+#else
+global using Unsafe = System.Runtime.CompilerServices.Unsafe;
+#endif
+
+#pragma warning restore IDE0005
+
+#if UNSAFE_IN_ILHELPERS
+// SRCS.Unsafe is defined in ILHelpers, so we want to define UnsafeRaw + a type-forwarder
+
+#if NET6_0_OR_GREATER
+using ILImpl = System.Runtime.CompilerServices.Unsafe;
+#else
+using ILImpl = ilhelpers::System.Runtime.CompilerServices.Unsafe;
+#endif
+
+using System;
+using System.Runtime.CompilerServices;
+
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(ILImpl))]
+
+namespace MonoMod.Backports.ILHelpers;
+
+[CLSCompliant(false)]
+public static unsafe class UnsafeRaw
+#else
+// SRCS.Unsafe is defined here, so we want to define Unsafe
+
+using MonoMod.Backports;
+
+using ILImpl = ilhelpers::MonoMod.Backports.ILHelpers.UnsafeRaw;
+
+namespace System.Runtime.CompilerServices;
+
+[CLSCompliant(false)]
+public static unsafe class Unsafe
+#endif
+{
+ #region Direct forwarders
+#nullable disable
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static T Read(void* source) => ILImpl.Read(source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static T ReadUnaligned(void* source) => ILImpl.ReadUnaligned(source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static T ReadUnaligned(ref byte source) => ILImpl.ReadUnaligned(ref source);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void Write(void* destination, T value) => ILImpl.Write(destination, value);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void WriteUnaligned(void* destination, T value) => ILImpl.WriteUnaligned(destination, value);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void WriteUnaligned(ref byte destination, T value) => ILImpl.WriteUnaligned(ref destination, value);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void Copy(void* destination, ref T source) => ILImpl.Copy(destination, ref source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void Copy(ref T destination, void* source) => ILImpl.Copy(ref destination, source);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void* AsPointer(ref T value) => ILImpl.AsPointer(ref value);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void SkipInit(out T value) => ILImpl.SkipInit(out value);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void CopyBlock(void* destination, void* source, uint byteCount) => ILImpl.CopyBlock(destination, source, byteCount);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void CopyBlock(ref byte destination, ref byte source, uint byteCount) => ILImpl.CopyBlock(ref destination, ref source, byteCount);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void CopyBlockUnaligned(void* destination, void* source, uint byteCount) => ILImpl.CopyBlockUnaligned(destination, source, byteCount);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount) => ILImpl.CopyBlockUnaligned(ref destination, ref source, byteCount);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void InitBlock(void* startAddress, byte value, uint byteCount) => ILImpl.InitBlock(startAddress, value, byteCount);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void InitBlock(ref byte startAddress, byte value, uint byteCount) => ILImpl.InitBlock(ref startAddress, value, byteCount);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) => ILImpl.InitBlockUnaligned(startAddress, value, byteCount);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) => ILImpl.InitBlockUnaligned(ref startAddress, value, byteCount);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static T As(object o) where T : class => ILImpl.As(o);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T AsRef(void* source) => ref ILImpl.AsRef(source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T AsRef(in T source) => ref ILImpl.AsRef(in source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref TTo As(ref TFrom source) => ref ILImpl.As(ref source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Unbox(object box) where T : struct => ref ILImpl.Unbox(box);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T AddByteOffset(ref T source, nint byteOffset) => ref ILImpl.AddByteOffset(ref source, byteOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T AddByteOffset(ref T source, nuint byteOffset) => ref ILImpl.AddByteOffset(ref source, byteOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T SubtractByteOffset(ref T source, nint byteOffset) => ref ILImpl.SubtractByteOffset(ref source, byteOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T SubtractByteOffset(ref T source, nuint byteOffset) => ref ILImpl.SubtractByteOffset(ref source, byteOffset);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static nint ByteOffset(ref T origin, ref T target) => ILImpl.ByteOffset(ref origin, ref target);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static bool AreSame(ref T left, ref T right) => ILImpl.AreSame(ref left, ref right);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static bool IsAddressGreaterThan(ref T left, ref T right) => ILImpl.IsAddressGreaterThan(ref left, ref right);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static bool IsAddressLessThan(ref T left, ref T right) => ILImpl.IsAddressLessThan(ref left, ref right);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static bool IsNullRef(ref T source) => ILImpl.IsNullRef(ref source);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T NullRef() => ref ILImpl.NullRef();
+#nullable enable
+ #endregion
+
+#if !UNSAFE_IN_ILHELPERS
+ // See docs/RuntimeIssueNotes.md. Until 2015, Mono returned incorrect values for the sizeof opcode when applied to a type parameter.
+ // To deal with this, we need to compute type size in another way, and return it as appropriate, specializing all of the below accordingly.
+ private static class PerTypeValues
+ {
+ public static readonly nint TypeSize = ComputeTypeSize();
+
+ private static nint ComputeTypeSize()
+ {
+ var array = new T[2];
+ return ILImpl.ByteOffset(ref array[0], ref array[1]);
+ }
+ }
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static int SizeOf() => (int)PerTypeValues.TypeSize;
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Add(ref T source, int elementOffset) => ref ILImpl.AddByteOffset(ref source, (nint)elementOffset * PerTypeValues.TypeSize);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void* Add(void* source, int elementOffset) => (byte*)source + (elementOffset * PerTypeValues.TypeSize);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Add(ref T source, nint elementOffset) => ref ILImpl.AddByteOffset(ref source, elementOffset * PerTypeValues.TypeSize);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Add(ref T source, nuint elementOffset) => ref ILImpl.AddByteOffset(ref source, elementOffset * (nuint)PerTypeValues.TypeSize);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Subtract(ref T source, int elementOffset) => ref ILImpl.SubtractByteOffset(ref source, (nint)elementOffset * PerTypeValues.TypeSize);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void* Subtract(void* source, int elementOffset) => (byte*)source - (elementOffset * PerTypeValues.TypeSize);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Subtract(ref T source, nint elementOffset) => ref ILImpl.SubtractByteOffset(ref source, elementOffset * PerTypeValues.TypeSize);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Subtract(ref T source, nuint elementOffset) => ref ILImpl.SubtractByteOffset(ref source, elementOffset * (nuint)PerTypeValues.TypeSize);
+
+#else
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static int SizeOf() => ILImpl.SizeOf();
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Add(ref T source, int elementOffset) => ref ILImpl.Add(ref source, elementOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void* Add(void* source, int elementOffset) => ILImpl.Add(source, elementOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Add(ref T source, nint elementOffset) => ref ILImpl.Add(ref source, elementOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Add(ref T source, nuint elementOffset) => ref ILImpl.Add(ref source, elementOffset);
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Subtract(ref T source, int elementOffset) => ref ILImpl.Subtract(ref source, elementOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static void* Subtract(void* source, int elementOffset) => ILImpl.Subtract(source, elementOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Subtract(ref T source, nint elementOffset) => ref ILImpl.Subtract(ref source, elementOffset);
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable]
+ public static ref T Subtract(ref T source, nuint elementOffset) => ref ILImpl.Subtract(ref source, elementOffset);
+
+#endif
+}
\ No newline at end of file
diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs
index f307c323..114b4f07 100644
--- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs
+++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs
@@ -279,7 +279,7 @@ public Span Span
// and then cast to a Memory. Such a cast can only be done with unsafe or marshaling code,
// in which case that's the dangerous operation performed by the dev, and we're just following
// suit here to make it work as best as possible.
- return new Span(Unsafe.As>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length);
+ return new Span(s, (nint)RuntimeHelpers.OffsetToStringData, s.Length).Slice(_index, _length);
}
else if (_object != null)
{
diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs
index 9c065649..94ea033b 100644
--- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs
+++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs
@@ -278,7 +278,7 @@ public static ReadOnlySpan AsSpan(this string? text)
if (text == null)
return default;
- return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment, text.Length);
+ return new ReadOnlySpan(text, (nint)RuntimeHelpers.OffsetToStringData, text.Length);
}
///
@@ -302,7 +302,7 @@ public static ReadOnlySpan AsSpan(this string? text, int start)
if ((uint)start > (uint)text.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
- return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * sizeof(char), text.Length - start);
+ return new ReadOnlySpan(text, (nint)RuntimeHelpers.OffsetToStringData + start * sizeof(char), text.Length - start);
}
///
@@ -327,7 +327,7 @@ public static ReadOnlySpan AsSpan(this string? text, int start, int length
if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
- return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * sizeof(char), length);
+ return new ReadOnlySpan(text, (nint)RuntimeHelpers.OffsetToStringData + start * sizeof(char), length);
}
/// Creates a new over the portion of the target string.
@@ -383,19 +383,5 @@ public static ReadOnlyMemory AsMemory(this string? text, int start, int le
return new ReadOnlyMemory(text, start, length);
}
-
- internal static readonly nint StringAdjustment = MeasureStringAdjustment();
-
- private static nint MeasureStringAdjustment()
- {
- string sampleString = "a";
- unsafe
- {
- fixed (char* pSampleString = sampleString)
- {
- return Unsafe.ByteOffset(ref Unsafe.As>(sampleString).Data, ref Unsafe.AsRef(pSampleString));
- }
- }
- }
}
}
\ No newline at end of file
diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs
deleted file mode 100644
index 84e7e1b5..00000000
--- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-
-namespace System
-{
- //
- // This class exists solely so that arbitrary objects can be Unsafe-casted to it to get a ref to the start of the user data.
- //
- [StructLayout(LayoutKind.Sequential)]
- [SuppressMessage("Performance", "CA1812", Justification = "Objects are unsafe-casted to this to be stored in Memory and Span")]
- internal sealed class Pinnable
- {
- public T Data = default!;
- }
-}
\ No newline at end of file
diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs
index 10178db3..c4801fee 100644
--- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs
+++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs
@@ -196,7 +196,7 @@ public ReadOnlySpan Span
else if (typeof(T) == typeof(char) && _object is string s)
{
Debug.Assert(_length >= 0);
- return new ReadOnlySpan(Unsafe.As>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length);
+ return new ReadOnlySpan(s, (nint)RuntimeHelpers.OffsetToStringData, s.Length).Slice(_index, _length);
}
else if (_object != null)
{
diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs
index 50b15cb3..49525c36 100644
--- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs
+++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs
@@ -33,7 +33,7 @@ public ReadOnlySpan(T[]? array)
}
_length = array.Length;
- _pinnable = Unsafe.As>(array);
+ _pinnable = array;
_byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment;
}
@@ -63,7 +63,7 @@ public ReadOnlySpan(T[]? array, int start, int length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
_length = length;
- _pinnable = Unsafe.As>(array);
+ _pinnable = array;
_byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start);
}
@@ -97,7 +97,7 @@ public unsafe ReadOnlySpan(void* pointer, int length)
// Constructor for internal use only.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan(Pinnable? pinnable, IntPtr byteOffset, int length)
+ internal ReadOnlySpan(object? pinnable, IntPtr byteOffset, int length)
{
Debug.Assert(length >= 0);
@@ -122,10 +122,7 @@ public ref readonly T this[int index]
if ((uint)index >= ((uint)_length))
ThrowHelper.ThrowIndexOutOfRangeException();
- if (_pinnable == null)
- unsafe { return ref Unsafe.Add(ref Unsafe.AsRef(_byteOffset.ToPointer()), index); }
- else
- return ref Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index);
+ return ref Unsafe.Add(ref DangerousGetPinnableReference(), index);
}
}
@@ -138,11 +135,7 @@ public unsafe ref readonly T GetPinnableReference()
{
if (_length != 0)
{
- if (_pinnable == null)
- {
- return ref Unsafe.AsRef(_byteOffset.ToPointer());
- }
- return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset);
+ return ref DangerousGetPinnableReference();
}
return ref Unsafe.AsRef(null);
}
@@ -205,7 +198,7 @@ public override string ToString()
if (typeof(T) == typeof(char))
{
// If this wraps a string and represents the full length of the string, just return the wrapped string.
- if (_byteOffset == MemoryExtensions.StringAdjustment)
+ if (_byteOffset == (nint)RuntimeHelpers.OffsetToStringData)
{
object? obj = Unsafe.As
\ No newline at end of file
diff --git a/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs b/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs
index 9e705963..a30ace63 100644
--- a/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs
+++ b/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs
@@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using CallSite = Mono.Cecil.CallSite;
namespace MonoMod.Utils
@@ -49,7 +49,7 @@ private static readonly MethodInfo? mDynamicMethod_AddRef
.GetType("System.Reflection.Emit.DynamicScope")?.GetField("m_tokens", BindingFlags.NonPublic | BindingFlags.Instance);
// Based on https://referencesource.microsoft.com/#mscorlib/system/reflection/mdimport.cs,74bfbae3c61889bc
- private static readonly Type?[] CorElementTypes = new Type?[] {
+ private static readonly Type?[] CorElementTypes = [
null, // END
typeof(void), // VOID
typeof(bool), // BOOL
@@ -80,7 +80,7 @@ private static readonly MethodInfo? mDynamicMethod_AddRef
null, // FNPTR
typeof(object), // OBJECT
// all others don't have specific types associated
- };
+ ];
private abstract class TokenCreator
{
@@ -133,266 +133,274 @@ public override int GetTokenForSig(byte[] sig)
// I assume, however, that we can't use SignatureHelper here because it is horribly broken on some (probably older) mono builds.
}
- internal static void _EmitCallSite(DynamicMethod dm, ILGenerator il, System.Reflection.Emit.OpCode opcode, CallSite csite)
+ private abstract class CallSiteEmitter
{
- /* The mess in this method is heavily based off of the code available at the following links:
- * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicmethod.cs#L791
- * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicilgenerator.cs#L353
- * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs#L411
- * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/ILGenerator.cs#L800
- * https://github.com/dotnet/coreclr/blob/0fbd855e38bc3ec269479b5f6bf561dcfd67cbb6/src/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs#L57
- */
+ public abstract void EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite);
+ }
- TokenCreator tokenCreator = DynamicMethod_AddRef is not null
- ? new MonoTokenCreator(dm) : new NetTokenCreator(il);
+ private sealed class NetCallSiteEmitter : CallSiteEmitter
+ {
+ public override void EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite)
+ {
+ /* The mess in this method is heavily based off of the code available at the following links:
+ * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicmethod.cs#L791
+ * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicilgenerator.cs#L353
+ * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs#L411
+ * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/ILGenerator.cs#L800
+ * https://github.com/dotnet/coreclr/blob/0fbd855e38bc3ec269479b5f6bf561dcfd67cbb6/src/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs#L57
+ */
- var signature = new byte[32];
- var currSig = 0;
- var sizeLoc = -1;
+ TokenCreator tokenCreator = DynamicMethod_AddRef is not null
+ ? new MonoTokenCreator(dm) : new NetTokenCreator(il);
- // This expects a MdSigCallingConvention
- AddData((byte)csite.CallingConvention);
- sizeLoc = currSig++;
+ var signature = new byte[32];
+ var currSig = 0;
+ var sizeLoc = -1;
- var modReq = new List();
- var modOpt = new List();
+ // We're emitting a StandAloneMethodSig
- ResolveWithModifiers(csite.ReturnType, out var returnType, out var returnTypeModReq, out var returnTypeModOpt, modReq, modOpt);
- AddArgument(returnType, returnTypeModReq, returnTypeModOpt);
+ AddData(((byte)csite.CallingConvention) | (csite.HasThis ? 0x20 : 0) | (csite.ExplicitThis ? 0x40 : 0));
+ sizeLoc = currSig++;
- foreach (var param in csite.Parameters)
- {
- if (param.ParameterType.IsSentinel)
- AddElementType(0x41 /* CorElementType.Sentinel */);
+ var modReq = new List();
+ var modOpt = new List();
- if (param.ParameterType.IsPinned)
- {
- AddElementType(0x45 /* CorElementType.Pinned */);
- // AddArgument(param.ParameterType.ResolveReflection());
- // continue;
- }
+ ResolveWithModifiers(csite.ReturnType, out var returnType, out var returnTypeModReq, out var returnTypeModOpt, modReq, modOpt);
+ AddArgument(returnType, returnTypeModReq, returnTypeModOpt);
- ResolveWithModifiers(param.ParameterType, out var paramType, out var paramTypeModReq, out var paramTypeModOpt, modReq, modOpt);
- AddArgument(paramType, paramTypeModReq, paramTypeModOpt);
- }
+ foreach (var param in csite.Parameters)
+ {
+ if (param.ParameterType.IsSentinel)
+ AddElementType(0x41 /* CorElementType.Sentinel */);
- AddElementType(0x00 /* CorElementType.End */);
-
- // For most signatures, this will set the number of elements in a byte which we have reserved for it.
- // However, if we have a field signature, we don't set the length and return.
- // If we have a signature with more than 128 arguments, we can't just set the number of elements,
- // we actually have to allocate more space (e.g. shift everything in the array one or more spaces to the
- // right. We do this by making a copy of the array and leaving the correct number of blanks. This new
- // array is now set to be m_signature and we use the AddData method to set the number of elements properly.
- // The forceCopy argument can be used to force SetNumberOfSignatureElements to make a copy of
- // the array. This is useful for GetSignature which promises to trim the array to be the correct size anyway.
-
- byte[] temp;
- int newSigSize;
- var currSigHolder = currSig;
-
- // We need to have more bytes for the size. Figure out how many bytes here.
- // Since we need to copy anyway, we're just going to take the cost of doing a
- // new allocation.
- if (csite.Parameters.Count < 0x80)
- {
- newSigSize = 1;
- }
- else if (csite.Parameters.Count < 0x4000)
- {
- newSigSize = 2;
- }
- else
- {
- newSigSize = 4;
- }
+ if (param.ParameterType.IsPinned)
+ {
+ AddElementType(0x45 /* CorElementType.Pinned */);
+ // AddArgument(param.ParameterType.ResolveReflection());
+ // continue;
+ }
- // Allocate the new array.
- temp = new byte[currSig + newSigSize - 1];
+ ResolveWithModifiers(param.ParameterType, out var paramType, out var paramTypeModReq, out var paramTypeModOpt, modReq, modOpt);
+ AddArgument(paramType, paramTypeModReq, paramTypeModOpt);
+ }
- // Copy the calling convention. The calling convention is always just one byte
- // so we just copy that byte. Then copy the rest of the array, shifting everything
- // to make room for the new number of elements.
- temp[0] = signature[0];
- Buffer.BlockCopy(signature, sizeLoc + 1, temp, sizeLoc + newSigSize, currSigHolder - (sizeLoc + 1));
- signature = temp;
+ AddElementType(0x00 /* CorElementType.End */);
+
+ // For most signatures, this will set the number of elements in a byte which we have reserved for it.
+ // However, if we have a field signature, we don't set the length and return.
+ // If we have a signature with more than 128 arguments, we can't just set the number of elements,
+ // we actually have to allocate more space (e.g. shift everything in the array one or more spaces to the
+ // right. We do this by making a copy of the array and leaving the correct number of blanks. This new
+ // array is now set to be m_signature and we use the AddData method to set the number of elements properly.
+ // The forceCopy argument can be used to force SetNumberOfSignatureElements to make a copy of
+ // the array. This is useful for GetSignature which promises to trim the array to be the correct size anyway.
+
+ byte[] temp;
+ int newSigSize;
+ var currSigHolder = currSig;
+
+ // We need to have more bytes for the size. Figure out how many bytes here.
+ // Since we need to copy anyway, we're just going to take the cost of doing a
+ // new allocation.
+ if (csite.Parameters.Count < 0x80)
+ {
+ newSigSize = 1;
+ }
+ else if (csite.Parameters.Count < 0x4000)
+ {
+ newSigSize = 2;
+ }
+ else
+ {
+ newSigSize = 4;
+ }
- //Use the AddData method to add the number of elements appropriately compressed.
- currSig = sizeLoc;
- AddData(csite.Parameters.Count);
- currSig = currSigHolder + (newSigSize - 1);
+ // Allocate the new array.
+ temp = new byte[currSig + newSigSize - 1];
- // This case will only happen if the user got the signature through
- // InternalGetSignature first and then called GetSignature.
- if (signature.Length > currSig)
- {
- temp = new byte[currSig];
- Array.Copy(signature, temp, currSig);
+ // Copy the calling convention. The calling convention is always just one byte
+ // so we just copy that byte. Then copy the rest of the array, shifting everything
+ // to make room for the new number of elements.
+ temp[0] = signature[0];
+ Buffer.BlockCopy(signature, sizeLoc + 1, temp, sizeLoc + newSigSize, currSigHolder - (sizeLoc + 1));
signature = temp;
- }
- // Emit.
+ //Use the AddData method to add the number of elements appropriately compressed.
+ currSig = sizeLoc;
+ AddData(csite.Parameters.Count);
+ currSig = currSigHolder + (newSigSize - 1);
- if (_ILGen_emit_int != null)
- {
- // Mono
- _ILGen_make_room!.Invoke(il, new object[] { 6 });
- _ILGen_ll_emit!.Invoke(il, new object[] { opcode });
- _ILGen_emit_int!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) });
- }
- else
- {
- // .NET
- _ILGen_EnsureCapacity!.Invoke(il, new object[] { 7 });
- _ILGen_InternalEmit!.Invoke(il, new object[] { opcode });
-
- // The only IL instruction that has VarPop behaviour, that takes a
- // Signature token as a parameter is calli. Pop the parameters and
- // the native function pointer. To be conservative, do not pop the
- // this pointer since this information is not easily derived from
- // SignatureHelper.
- if (opcode.StackBehaviourPop == System.Reflection.Emit.StackBehaviour.Varpop)
+ // This case will only happen if the user got the signature through
+ // InternalGetSignature first and then called GetSignature.
+ if (signature.Length > currSig)
{
- // Pop the arguments and native function pointer off the stack.
- _ILGen_UpdateStackSize!.Invoke(il, new object[] { opcode, -csite.Parameters.Count - 1 });
+ temp = new byte[currSig];
+ Array.Copy(signature, temp, currSig);
+ signature = temp;
}
- _ILGen_PutInteger4!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) });
- }
+ // Emit.
- void AddArgument(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
- {
- if (optionalCustomModifiers != null)
- foreach (var t in optionalCustomModifiers)
- InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x20 /* CorElementType.CModOpt */);
+ if (_ILGen_emit_int != null)
+ {
+ // Mono
+ _ILGen_make_room!.Invoke(il, new object[] { 6 });
+ _ILGen_ll_emit!.Invoke(il, new object[] { opcode });
+ _ILGen_emit_int!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) });
+ }
+ else
+ {
+ // .NET
+ _ILGen_EnsureCapacity!.Invoke(il, new object[] { 7 });
+ _ILGen_InternalEmit!.Invoke(il, new object[] { opcode });
+
+ // The only IL instruction that has VarPop behaviour, that takes a
+ // Signature token as a parameter is calli. Pop the parameters and
+ // the native function pointer. To be conservative, do not pop the
+ // this pointer since this information is not easily derived from
+ // SignatureHelper.
+ if (opcode.StackBehaviourPop == System.Reflection.Emit.StackBehaviour.Varpop)
+ {
+ // Pop the arguments and native function pointer off the stack.
+ _ILGen_UpdateStackSize!.Invoke(il, new object[] { opcode, -csite.Parameters.Count - 1 });
+ }
- if (requiredCustomModifiers != null)
- foreach (var t in requiredCustomModifiers)
- InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x1F /* CorElementType.CModReqd */);
+ _ILGen_PutInteger4!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) });
+ }
- AddOneArgTypeHelper(clsArgument);
- }
+ void AddArgument(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
+ {
+ if (optionalCustomModifiers != null)
+ foreach (var t in optionalCustomModifiers)
+ InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x20 /* CorElementType.CModOpt */);
- void AddData(int data)
- {
- // A managed representation of CorSigCompressData;
+ if (requiredCustomModifiers != null)
+ foreach (var t in requiredCustomModifiers)
+ InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x1F /* CorElementType.CModReqd */);
- if (currSig + 4 > signature!.Length)
- {
- signature = ExpandArray(signature);
+ AddOneArgTypeHelper(clsArgument);
}
- if (data <= 0x7F)
+ void AddData(int data)
{
- signature[currSig++] = (byte)(data & 0xFF);
- }
- else if (data <= 0x3FFF)
- {
- signature[currSig++] = (byte)((data >> 8) | 0x80);
- signature[currSig++] = (byte)(data & 0xFF);
+ // A managed representation of CorSigCompressData;
+
+ if (currSig + 4 > signature!.Length)
+ {
+ signature = ExpandArray(signature);
+ }
+
+ if (data <= 0x7F)
+ {
+ signature[currSig++] = (byte)(data & 0xFF);
+ }
+ else if (data <= 0x3FFF)
+ {
+ signature[currSig++] = (byte)((data >> 8) | 0x80);
+ signature[currSig++] = (byte)(data & 0xFF);
+ }
+ else if (data <= 0x1FFFFFFF)
+ {
+ signature[currSig++] = (byte)((data >> 24) | 0xC0);
+ signature[currSig++] = (byte)((data >> 16) & 0xFF);
+ signature[currSig++] = (byte)((data >> 8) & 0xFF);
+ signature[currSig++] = (byte)((data) & 0xFF);
+ }
+ else
+ {
+ throw new ArgumentException("Integer or token was too large to be encoded.");
+ }
}
- else if (data <= 0x1FFFFFFF)
+
+ byte[] ExpandArray(byte[] inArray, int requiredLength = -1)
{
- signature[currSig++] = (byte)((data >> 24) | 0xC0);
- signature[currSig++] = (byte)((data >> 16) & 0xFF);
- signature[currSig++] = (byte)((data >> 8) & 0xFF);
- signature[currSig++] = (byte)((data) & 0xFF);
+ if (requiredLength < inArray.Length)
+ requiredLength = inArray.Length * 2;
+
+ var outArray = new byte[requiredLength];
+ Buffer.BlockCopy(inArray, 0, outArray, 0, inArray.Length);
+ return outArray;
}
- else
+
+ void AddElementType(byte cvt)
{
- throw new ArgumentException("Integer or token was too large to be encoded.");
- }
- }
+ // Adds an element to the signature. A managed represenation of CorSigCompressElement
+ if (currSig + 1 > signature.Length)
+ signature = ExpandArray(signature);
- byte[] ExpandArray(byte[] inArray, int requiredLength = -1)
- {
- if (requiredLength < inArray.Length)
- requiredLength = inArray.Length * 2;
+ signature[currSig++] = cvt;
+ }
- var outArray = new byte[requiredLength];
- Buffer.BlockCopy(inArray, 0, outArray, 0, inArray.Length);
- return outArray;
- }
+ void AddToken(int token)
+ {
+ // A managed represenation of CompressToken
+ // Pulls the token appart to get a rid, adds some appropriate bits
+ // to the token and then adds this to the signature.
- void AddElementType(byte cvt)
- {
- // Adds an element to the signature. A managed represenation of CorSigCompressElement
- if (currSig + 1 > signature.Length)
- signature = ExpandArray(signature);
+ var rid = (token & 0x00FFFFFF); //This is RidFromToken;
+ var type = (token & unchecked((int)0xFF000000)); //This is TypeFromToken;
- signature[currSig++] = cvt;
- }
+ if (rid > 0x3FFFFFF)
+ {
+ // token is too big to be compressed
+ throw new ArgumentException("Integer or token was too large to be encoded.");
+ }
- void AddToken(int token)
- {
- // A managed represenation of CompressToken
- // Pulls the token appart to get a rid, adds some appropriate bits
- // to the token and then adds this to the signature.
+ rid = (rid << 2);
- var rid = (token & 0x00FFFFFF); //This is RidFromToken;
- var type = (token & unchecked((int)0xFF000000)); //This is TypeFromToken;
+ // TypeDef is encoded with low bits 00
+ // TypeRef is encoded with low bits 01
+ // TypeSpec is encoded with low bits 10
+ if (type == 0x01000000 /* MetadataTokenType.TypeRef */)
+ {
+ //if type is mdtTypeRef
+ rid |= 0x1;
+ }
+ else if (type == 0x1b000000 /* MetadataTokenType.TypeSpec */)
+ {
+ //if type is mdtTypeSpec
+ rid |= 0x2;
+ }
- if (rid > 0x3FFFFFF)
- {
- // token is too big to be compressed
- throw new ArgumentException("Integer or token was too large to be encoded.");
+ AddData(rid);
}
- rid = (rid << 2);
-
- // TypeDef is encoded with low bits 00
- // TypeRef is encoded with low bits 01
- // TypeSpec is encoded with low bits 10
- if (type == 0x01000000 /* MetadataTokenType.TypeRef */)
+ void InternalAddTypeToken(int clsToken, byte CorType)
{
- //if type is mdtTypeRef
- rid |= 0x1;
- }
- else if (type == 0x1b000000 /* MetadataTokenType.TypeSpec */)
- {
- //if type is mdtTypeSpec
- rid |= 0x2;
+ // Add a type token into signature. CorType will be either CorElementType.Class or CorElementType.ValueType
+ AddElementType(CorType);
+ AddToken(clsToken);
}
- AddData(rid);
- }
-
- void InternalAddTypeToken(int clsToken, byte CorType)
- {
- // Add a type token into signature. CorType will be either CorElementType.Class or CorElementType.ValueType
- AddElementType(CorType);
- AddToken(clsToken);
- }
-
- void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); }
- void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst)
- {
- if (clsArgument.IsGenericType && (!clsArgument.IsGenericTypeDefinition || !lastWasGenericInst))
+ void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); }
+ void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst)
{
- AddElementType(0x15 /* CorElementType.GenericInst */);
+ if (clsArgument.IsGenericType && (!clsArgument.IsGenericTypeDefinition || !lastWasGenericInst))
+ {
+ AddElementType(0x15 /* CorElementType.GenericInst */);
- AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true);
+ AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true);
- var genargs = clsArgument.GetGenericArguments();
+ var genargs = clsArgument.GetGenericArguments();
- AddData(genargs.Length);
+ AddData(genargs.Length);
- foreach (var t in genargs)
- AddOneArgTypeHelper(t);
- }
- else if (clsArgument.IsByRef)
- {
- AddElementType(0x10 /* CorElementType.ByRef */);
- clsArgument = clsArgument.GetElementType() ?? clsArgument;
- AddOneArgTypeHelper(clsArgument);
- }
- else if (clsArgument.IsPointer)
- {
- AddElementType(0x0F /* CorElementType.Ptr */);
- AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument);
- }
- else if (clsArgument.IsArray)
- {
+ foreach (var t in genargs)
+ AddOneArgTypeHelper(t);
+ }
+ else if (clsArgument.IsByRef)
+ {
+ AddElementType(0x10 /* CorElementType.ByRef */);
+ clsArgument = clsArgument.GetElementType() ?? clsArgument;
+ AddOneArgTypeHelper(clsArgument);
+ }
+ else if (clsArgument.IsPointer)
+ {
+ AddElementType(0x0F /* CorElementType.Ptr */);
+ AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument);
+ }
+ else if (clsArgument.IsArray)
+ {
#if false
if (clsArgument.IsArray && clsArgument == clsArgument.GetElementType().MakeArrayType()) { // .IsSZArray unavailable.
AddElementType(0x1D /* CorElementType.SzArray */);
@@ -400,71 +408,165 @@ void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst)
AddOneArgTypeHelper(clsArgument.GetElementType());
} else
#endif
- {
- AddElementType(0x14 /* CorElementType.Array */);
+ {
+ AddElementType(0x14 /* CorElementType.Array */);
- AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument);
+ AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument);
- // put the rank information
- var rank = clsArgument.GetArrayRank();
- AddData(rank); // rank
- AddData(0); // upper bounds
- AddData(rank); // lower bound
- for (var i = 0; i < rank; i++)
- AddData(0);
+ // put the rank information
+ var rank = clsArgument.GetArrayRank();
+ AddData(rank); // rank
+ AddData(0); // upper bounds
+ AddData(rank); // lower bound
+ for (var i = 0; i < rank; i++)
+ AddData(0);
+ }
}
- }
- else
- {
- // This isn't 100% accurate, but... oh well.
- byte type = 0; // 0 is reserved anyway.
-
- for (var i = 0; i < CorElementTypes.Length; i++)
+ else
{
- if (clsArgument == CorElementTypes[i])
+ // This isn't 100% accurate, but... oh well.
+ byte type = 0; // 0 is reserved anyway.
+
+ for (var i = 0; i < CorElementTypes.Length; i++)
{
- type = (byte)i;
- break;
+ if (clsArgument == CorElementTypes[i])
+ {
+ type = (byte)i;
+ break;
+ }
}
- }
- if (type == 0)
- {
- if (clsArgument == typeof(object))
+ if (type == 0)
+ {
+ if (clsArgument == typeof(object))
+ {
+ type = 0x1C /* CorElementType.Object */;
+ }
+ else if (clsArgument.IsValueType)
+ {
+ type = 0x11 /* CorElementType.ValueType */;
+ }
+ else
+ {
+ // Let's hope for the best.
+ type = 0x12 /* CorElementType.Class */;
+ }
+ }
+
+ if (type <= 0x0E /* CorElementType.String */ ||
+ type == 0x16 /* CorElementType.TypedByRef */ ||
+ type == 0x18 /* CorElementType.I */ ||
+ type == 0x19 /* CorElementType.U */ ||
+ type == 0x1C /* CorElementType.Object */
+ )
{
- type = 0x1C /* CorElementType.Object */;
+ AddElementType(type);
}
else if (clsArgument.IsValueType)
{
- type = 0x11 /* CorElementType.ValueType */;
+ InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x11 /* CorElementType.ValueType */);
}
else
{
- // Let's hope for the best.
- type = 0x12 /* CorElementType.Class */;
+ InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x12 /* CorElementType.Class */);
}
}
+ }
+ }
+ }
- if (type <= 0x0E /* CorElementType.String */ ||
- type == 0x16 /* CorElementType.TypedByRef */ ||
- type == 0x18 /* CorElementType.I */ ||
- type == 0x19 /* CorElementType.U */ ||
- type == 0x1C /* CorElementType.Object */
- )
- {
- AddElementType(type);
- }
- else if (clsArgument.IsValueType)
- {
- InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x11 /* CorElementType.ValueType */);
- }
- else
- {
- InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x12 /* CorElementType.Class */);
- }
+ private sealed class MonoCallSiteEmitter : CallSiteEmitter
+ {
+ private FieldInfo SigHelper_callConv;
+ private FieldInfo SigHelper_unmanagedCallConv;
+ private FieldInfo SigHelper_arguments;
+ private FieldInfo SigHelper_modreqs;
+ private FieldInfo SigHelper_modopts;
+
+ public MonoCallSiteEmitter()
+ {
+ var callConv = typeof(SignatureHelper).GetField("callConv", BindingFlags.Instance | BindingFlags.NonPublic);
+ var unmanagedCallConv = typeof(SignatureHelper).GetField("unmanagedCallConv", BindingFlags.Instance | BindingFlags.NonPublic);
+ var arguments = typeof(SignatureHelper).GetField("arguments", BindingFlags.Instance | BindingFlags.NonPublic);
+ var modreqs = typeof(SignatureHelper).GetField("modreqs", BindingFlags.Instance | BindingFlags.NonPublic);
+ var modopts = typeof(SignatureHelper).GetField("modopts", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ // if we hit this ctor, we should be running on Mono, which should mean these are all present
+ Helpers.Assert(callConv is not null);
+ Helpers.Assert(unmanagedCallConv is not null);
+ Helpers.Assert(arguments is not null);
+ Helpers.Assert(modreqs is not null);
+ Helpers.Assert(modopts is not null);
+
+ SigHelper_callConv = callConv;
+ SigHelper_unmanagedCallConv = unmanagedCallConv;
+ SigHelper_arguments = arguments;
+ SigHelper_modreqs = modreqs;
+ SigHelper_modopts = modopts;
+ }
+
+ public override void EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite)
+ {
+ // On Mono, when its processing the tokens for a CallSite, it explicitly looks for a SignatureHelper, and so we CANNOT pass in
+ // a manually constructed signature. At all. Which sucks. It means that, on older Mono that have half-implemented SignatureHelpers,
+ // there's nothing we can do.
+
+ var modReq = new List();
+ var modOpt = new List();
+
+ // note: with the Mono signature helper, we can't represent modifiers on the return type
+ ResolveWithModifiers(csite.ReturnType, out var rawRetType, out _, out _, modReq, modOpt);
+
+ // there's not a standard, public API for the metadata callconv field either, so we have to set it manually
+ var sigHelper = SignatureHelper.GetMethodSigHelper(CallingConventions.Standard, rawRetType);
+
+ var arguments = new Type[csite.Parameters.Count];
+ var modreqs = new Type[csite.Parameters.Count][];
+ var modopts = new Type[csite.Parameters.Count][];
+
+ var managedCallConv = csite.CallingConvention switch
+ {
+ MethodCallingConvention.VarArg => CallingConventions.VarArgs,
+ _ => CallingConventions.Standard,
+ };
+ if (csite.HasThis) managedCallConv |= CallingConventions.HasThis;
+ if (csite.ExplicitThis) managedCallConv |= CallingConventions.ExplicitThis;
+
+ var unmanagedCallConv = csite.CallingConvention switch
+ {
+ MethodCallingConvention.C => CallingConvention.Cdecl,
+ MethodCallingConvention.StdCall => CallingConvention.StdCall,
+ MethodCallingConvention.ThisCall => CallingConvention.ThisCall,
+ MethodCallingConvention.FastCall => CallingConvention.FastCall,
+ _ => (CallingConvention)0,
+ };
+
+ for (var i = 0; i < csite.Parameters.Count; i++)
+ {
+ var param = csite.Parameters[i];
+
+ ResolveWithModifiers(param.ParameterType, out arguments[i], out modreqs[i], out modopts[i], modReq, modOpt);
}
+
+ // fill the signature helper
+ SigHelper_callConv.SetValue(sigHelper, managedCallConv);
+ SigHelper_unmanagedCallConv.SetValue(sigHelper, unmanagedCallConv);
+ SigHelper_arguments.SetValue(sigHelper, arguments);
+ SigHelper_modreqs.SetValue(sigHelper, modreqs);
+ SigHelper_modopts.SetValue(sigHelper, modopts);
+
+ // emit the sighelper
+ _ILGen_make_room!.Invoke(il, new object[] { 6 });
+ _ILGen_ll_emit!.Invoke(il, new object[] { opcode });
+ _ILGen_emit_int!.Invoke(il, new object[] { DynamicMethod_AddRef!(dm, sigHelper) });
}
+ }
+ private static readonly CallSiteEmitter callSiteEmitter = DynamicMethod_AddRef is not null ? new MonoCallSiteEmitter() : new NetCallSiteEmitter();
+
+ internal static void _EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite)
+ {
+ callSiteEmitter.EmitCallSite(dm, il, opcode, csite);
}
}
diff --git a/src/MonoMod.Utils/DynDll.Backend.cs b/src/MonoMod.Utils/DynDll.Backend.cs
index d47bd478..c8a3f548 100644
--- a/src/MonoMod.Utils/DynDll.Backend.cs
+++ b/src/MonoMod.Utils/DynDll.Backend.cs
@@ -181,7 +181,7 @@ protected override unsafe bool TryOpenLibraryCore(string? name, Assembly assembl
}
else
{
- fixed (char* pName = name)
+ fixed (char* pName = name.AsSpan())
{
handle = result = Interop.Windows.LoadLibraryW((ushort*)pName);
}
diff --git a/src/MonoMod.Utils/DynamicReferenceManager.cs b/src/MonoMod.Utils/DynamicReferenceManager.cs
index ce08140c..9396bfbd 100644
--- a/src/MonoMod.Utils/DynamicReferenceManager.cs
+++ b/src/MonoMod.Utils/DynamicReferenceManager.cs
@@ -114,7 +114,7 @@ private static unsafe DataScope AllocReferenceStruct(in
[MethodImpl(MethodImplOptionsEx.AggressiveOptimization)]
public static DataScope AllocReference(in T? value, out DynamicReferenceCell cellRef)
{
- if (default(T) == null)
+ if (!typeof(T).IsValueType)
{
return AllocReferenceClass(Unsafe.As(ref Unsafe.AsRef(in value)), out cellRef);
}
@@ -197,14 +197,14 @@ private static Cell GetCell(DynamicReferenceCell cellRef)
{
case RefValueCell:
{
- Helpers.Assert(default(T) == null);
+ Helpers.Assert(!typeof(T).IsValueType);
var c = Unsafe.As(cell);
Helpers.Assert(c.Value is null or T);
return ref Unsafe.As(ref c.Value!);
}
case ValueTypeCell:
{
- Helpers.Assert(default(T) != null);
+ Helpers.Assert(typeof(T).IsValueType);
var c = (ValueCell)cell;
return ref c.Value;
}
diff --git a/src/MonoMod.Utils/Extensions.Unsafe.cs b/src/MonoMod.Utils/Extensions.Unsafe.cs
index 54eeeeef..5df3b9e9 100644
--- a/src/MonoMod.Utils/Extensions.Unsafe.cs
+++ b/src/MonoMod.Utils/Extensions.Unsafe.cs
@@ -80,7 +80,7 @@ public static IntPtr GetLdftnPointer(this MethodBase m)
lock (_GetLdftnPointerCache)
{
- return (_GetLdftnPointerCache[m] = dmd.Generate().CreateDelegate>() as Func)();
+ return (_GetLdftnPointerCache[m] = dmd.Generate().CreateDelegate>())();
}
}
diff --git a/src/MonoMod.Utils/Helpers.cs b/src/MonoMod.Utils/Helpers.cs
index 96e736fc..5f4fb919 100644
--- a/src/MonoMod.Utils/Helpers.cs
+++ b/src/MonoMod.Utils/Helpers.cs
@@ -24,22 +24,22 @@ public static void Swap(ref T a, ref T b)
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public static unsafe bool Has(this T value, T flag) where T : struct, Enum
{
- if (sizeof(T) == sizeof(long))
+ if (Unsafe.SizeOf() == sizeof(long))
{
var flagVal = Unsafe.As(ref flag);
return (Unsafe.As(ref value) & flagVal) == flagVal;
}
- else if (sizeof(T) == sizeof(int))
+ else if (Unsafe.SizeOf() == sizeof(int))
{
var flagVal = Unsafe.As(ref flag);
return (Unsafe.As(ref value) & flagVal) == flagVal;
}
- else if (sizeof(T) == sizeof(short))
+ else if (Unsafe.SizeOf() == sizeof(short))
{
var flagVal = Unsafe.As(ref flag);
return (Unsafe.As(ref value) & flagVal) == flagVal;
}
- else if (sizeof(T) == sizeof(byte))
+ else if (Unsafe.SizeOf() == sizeof(byte))
{
var flagVal = Unsafe.As(ref flag);
return (Unsafe.As(ref value) & flagVal) == flagVal;
@@ -157,22 +157,48 @@ private static void ThrowAssertionFailed(ref AssertionInterpolatedStringHandler
}
#region GetOrInit*
+ private static class FuncInvokeHolder
+ {
+ public static readonly Func, T> InvokeFunc = static f => f();
+ }
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
+ public static T GetOrInit(ref T? location, Func init) where T : class
+ {
+ if (location is not null)
+ return location;
+ return InitializeValue(ref location, FuncInvokeHolder.InvokeFunc, init);
+ }
+
+ [MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
+ public static T GetOrInitWithLock(ref T? location, object @lock, Func init) where T : class
+ {
+ if (location is not null)
+ return location;
+ return InitializeValueWithLock(ref location, @lock, FuncInvokeHolder.InvokeFunc, init);
+ }
+
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
- public unsafe static T GetOrInit(ref T? location, Func init) where T : class
+ public static T GetOrInit(ref T? location, Func init, TParam param) where T : class
{
+ ThrowIfArgumentNull(init);
if (location is not null)
return location;
- return InitializeValue(ref location, &ILHelpers.TailCallFunc, init);
+ return InitializeValue(ref location, init, param);
}
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
- public unsafe static T GetOrInitWithLock(ref T? location, object @lock, Func init) where T : class
+ public static T GetOrInitWithLock(ref T? location, object @lock, Func init, TParam param) where T : class
{
+ ThrowIfArgumentNull(init);
if (location is not null)
return location;
- return InitializeValueWithLock(ref location, @lock, &ILHelpers.TailCallFunc, init);
+ return InitializeValueWithLock(ref location, @lock, init, param);
}
+ ///
+ /// This overload may not work on some older Mono implementations, which do not have good function pointer support.
+ ///
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public unsafe static T GetOrInit(ref T? location, delegate* init) where T : class
{
@@ -181,6 +207,10 @@ public unsafe static T GetOrInit(ref T? location, delegate* init) where T
return InitializeValue(ref location, &ILHelpers.TailCallDelegatePtr, (IntPtr)init);
}
+
+ ///
+ /// This overload may not work on some older Mono implementations, which do not have good function pointer support.
+ ///
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public unsafe static T GetOrInitWithLock(ref T? location, object @lock, delegate* init) where T : class
{
@@ -189,6 +219,9 @@ public unsafe static T GetOrInitWithLock(ref T? location, object @lock, deleg
return InitializeValueWithLock(ref location, @lock, &ILHelpers.TailCallDelegatePtr, (IntPtr)init);
}
+ ///
+ /// This overload may not work on some older Mono implementations, which do not have good function pointer support.
+ ///
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public unsafe static T GetOrInit(ref T? location, delegate* init, TParam obj) where T : class
{
@@ -197,6 +230,9 @@ public unsafe static T GetOrInit(ref T? location, delegate*
+ /// This overload may not work on some older Mono implementations, which do not have good function pointer support.
+ ///
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public unsafe static T GetOrInitWithLock(ref T? location, object @lock, delegate* init, TParam obj) where T : class
{
@@ -212,6 +248,13 @@ private unsafe static T InitializeValue(ref T? location, delegate*(ref T? location, Func init, TParam obj) where T : class
+ {
+ _ = Interlocked.CompareExchange(ref location, init(obj), null);
+ return location!;
+ }
+
[MethodImpl(MethodImplOptionsEx.NoInlining)]
private unsafe static T InitializeValueWithLock(ref T? location, object @lock, delegate* init, TParam obj) where T : class
{
@@ -222,6 +265,17 @@ private unsafe static T InitializeValueWithLock(ref T? location, obje
return location = init(obj);
}
}
+
+ [MethodImpl(MethodImplOptionsEx.NoInlining)]
+ private unsafe static T InitializeValueWithLock(ref T? location, object @lock, Func init, TParam obj) where T : class
+ {
+ lock (@lock)
+ {
+ if (location is not null)
+ return location;
+ return location = init(obj);
+ }
+ }
#endregion
[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
diff --git a/src/MonoMod.Utils/MonoMod.Utils.csproj b/src/MonoMod.Utils/MonoMod.Utils.csproj
index 1b58b7a4..84c5a57d 100644
--- a/src/MonoMod.Utils/MonoMod.Utils.csproj
+++ b/src/MonoMod.Utils/MonoMod.Utils.csproj
@@ -9,7 +9,7 @@
enable
false
- 25.0.4
+ 25.0.6
25.0.0
diff --git a/src/MonoMod.Utils/PlatformDetection.cs b/src/MonoMod.Utils/PlatformDetection.cs
index fc3e5a9f..d7eff378 100644
--- a/src/MonoMod.Utils/PlatformDetection.cs
+++ b/src/MonoMod.Utils/PlatformDetection.cs
@@ -398,7 +398,7 @@ private static unsafe bool CheckWine()
if (env == "FALSE")
return false;
- fixed (char* pNtdll = "ntdll.dll")
+ fixed (char* pNtdll = "ntdll.dll".AsSpan())
{
var ntdll = Interop.Windows.GetModuleHandleW((ushort*)pNtdll);
if (ntdll != Interop.Windows.HMODULE.NULL && ntdll != Interop.Windows.HMODULE.INVALID_VALUE)
diff --git a/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs b/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs
index b887663b..772dd288 100644
--- a/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs
+++ b/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs
@@ -7,18 +7,17 @@ namespace MonoMod.Utils
{
public static partial class ReflectionHelper
{
-
// .NET Framework can break member ordering if using Module.Resolve* on certain members.
private static readonly object?[] _CacheGetterArgs = { /* MemberListType.All */ 0, /* name apparently always null? */ null };
- private static Type t_RuntimeType =
+ // Note: on older Mono, RuntimeType doesn't exist. That's fine; it means there's no cache to fix.
+ private static Type? t_RuntimeType =
typeof(Type).Assembly
- .GetType("System.RuntimeType")
- ?? throw new InvalidOperationException("Could not find RuntimeType");
+ .GetType("System.RuntimeType");
private static Type? t_RuntimeTypeCache =
- t_RuntimeType.GetNestedType("RuntimeTypeCache", BindingFlags.Public | BindingFlags.NonPublic);
+ t_RuntimeType?.GetNestedType("RuntimeTypeCache", BindingFlags.Public | BindingFlags.NonPublic);
private static PropertyInfo? p_RuntimeType_Cache =
t_RuntimeTypeCache == null ? null :
diff --git a/tools/Common.CS.targets b/tools/Common.CS.targets
index 1b51c87a..4eccb1bc 100644
--- a/tools/Common.CS.targets
+++ b/tools/Common.CS.targets
@@ -48,8 +48,12 @@
Shared/%(Filename)%(Extension)
+
+
+
-
+
false
false
diff --git a/tools/Common.IL.targets b/tools/Common.IL.targets
index 31e8eba7..60debbaf 100644
--- a/tools/Common.IL.targets
+++ b/tools/Common.IL.targets
@@ -16,7 +16,7 @@
Reference="false" ReferenceOutputAssembly="false"
OutputItemType="ILVersionPatcher"
Private="false" Pack="false"
- SetTargetFramework="TargetFramework=net6.0"
+ SetTargetFramework="TargetFramework=net8.0"
SkipGetTargetFrameworkProperties="true" />
diff --git a/tools/Common.props b/tools/Common.props
index 86d1b347..42a70888 100644
--- a/tools/Common.props
+++ b/tools/Common.props
@@ -12,6 +12,7 @@
net6.0;net5.0;net7.0;net8.0;netstandard2.0;net35;net452
+ $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1
@@ -30,10 +31,8 @@
$(MMRootPath)docs\
- $(MMArtifactsPath)packages\$(Configuration)\
- $(MMArtifactsPath)obj\$(MSBuildProjectName)\
- $(BaseIntermediateOutputPath)$(Configuration)\
- $(MMArtifactsPath)bin\$(MSBuildProjectName)\
+ true
+ $(MMArtifactsPath)
true
false
@@ -43,6 +42,10 @@
true
false
false
+
+
+ false
+ true
diff --git a/tools/Common.targets b/tools/Common.targets
index 91dbe439..d69f5ce5 100644
--- a/tools/Common.targets
+++ b/tools/Common.targets
@@ -7,5 +7,20 @@
+
+
+
+ <_PropertiesToGet Remove="@(_PropertiesToGet)" />
+
+
+
+
+ <_PropertiesToGet Include="$(PropertyNames)" />
+
+
+
+
+
+
diff --git a/tools/NuGet.props b/tools/NuGet.props
index 07acb596..2642f90e 100644
--- a/tools/NuGet.props
+++ b/tools/NuGet.props
@@ -22,10 +22,6 @@
false
-
-
-
-
diff --git a/tools/NuGet.targets b/tools/NuGet.targets
index 8c6e1236..353e39e1 100644
--- a/tools/NuGet.targets
+++ b/tools/NuGet.targets
@@ -30,7 +30,7 @@
-
+
README.md
@@ -40,6 +40,11 @@
+
+
+
+
+
@@ -78,6 +83,7 @@
+
\ No newline at end of file
diff --git a/tools/tools.csproj b/tools/tools.csproj
index e00ea0ef..4d7b3159 100644
--- a/tools/tools.csproj
+++ b/tools/tools.csproj
@@ -1,2 +1,2 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tools/windbg-memlog.js b/tools/windbg-memlog.js
index 81e469fe..f389e8d4 100644
--- a/tools/windbg-memlog.js
+++ b/tools/windbg-memlog.js
@@ -1,4 +1,11 @@
-"use strict";
+"use strict";
+
+// To dump the memory log to a .tmp file
+// .scriptrun
+//
+// To get navigable version of the log
+// .scriptload //
+// dx -g @$scriptContents.GetMMLog()
function initializeScript()
{
@@ -11,6 +18,29 @@ function initializeScript()
return [new host.apiVersionSupport(1, 7)];
}
+function invokeScript()
+{
+ var log = GetMMLog();
+
+ var dbgOutput = host.diagnostics.debugLog;
+
+ var file = host.namespace.Debugger.Utility.FileSystem.CreateTempFile();
+ var textWriter = host.namespace.Debugger.Utility.FileSystem.CreateTextWriter(file);
+
+ dbgOutput("Dumping Memory log to tmp file\n");
+
+ for (const e of log) {
+ if (e.Index > 0 && e.Index % 1000 == 0)
+ dbgOutput(e.Index + "...\n");
+
+ textWriter.WriteLine("[" + e.Time + "] [" + e.Source + "] " + e.Level + ": " + e.Message);
+ }
+
+ file.Close();
+
+ dbgOutput("Dumped MonoMod Log to: " + file.Path);
+}
+
function GetMMLog()
{
var ctl = host.namespace.Debugger.Utility.Control;
@@ -235,5 +265,5 @@ function formatDateTime(dt) {
var date = isUtc
? new Date(Date.UTC(year, month - 1, day, hours, minutes, seconds, millis))
: new Date(year, month - 1, day, hours, minutes, seconds, millis);
- return date.toLocaleString();
+ return date.toLocaleString().replace(/[^\w:/\\ ]/g, "");
}
\ No newline at end of file